diff --git a/include/ignition/gazebo/rendering/RenderUtil.hh b/include/ignition/gazebo/rendering/RenderUtil.hh index ef10b2d93e..6110b2083c 100644 --- a/include/ignition/gazebo/rendering/RenderUtil.hh +++ b/include/ignition/gazebo/rendering/RenderUtil.hh @@ -83,7 +83,7 @@ inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE { /// \param[in] _ecm Const reference to the entity component manager /// \param[in] _entities Entities to create visuals for. public: void CreateVisualsForEntities(const EntityComponentManager &_ecm, - const std::set &_entities); + const std::set &_entities); /// \brief Set the rendering engine to use /// \param[in] _engineName Name of the rendering engine. diff --git a/src/gui/plugins/component_inspector/AirPressure.cc b/src/gui/plugins/component_inspector/AirPressure.cc index d9b9656598..c6487f53ef 100644 --- a/src/gui/plugins/component_inspector/AirPressure.cc +++ b/src/gui/plugins/component_inspector/AirPressure.cc @@ -68,7 +68,7 @@ Q_INVOKABLE void AirPressure::OnAirPressureNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::AirPressure *airpressure = comp->Data().AirPressureSensor(); @@ -100,7 +100,7 @@ Q_INVOKABLE void AirPressure::OnAirPressureReferenceAltitude( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::AirPressure *airpressure = comp->Data().AirPressureSensor(); diff --git a/src/gui/plugins/component_inspector/Altimeter.cc b/src/gui/plugins/component_inspector/Altimeter.cc index fca5263475..0179e81c39 100644 --- a/src/gui/plugins/component_inspector/Altimeter.cc +++ b/src/gui/plugins/component_inspector/Altimeter.cc @@ -72,7 +72,7 @@ Q_INVOKABLE void Altimeter::OnAltimeterPositionNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Altimeter *altimeter = comp->Data().AltimeterSensor(); @@ -106,7 +106,7 @@ Q_INVOKABLE void Altimeter::OnAltimeterVelocityNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Altimeter *altimeter = comp->Data().AltimeterSensor(); diff --git a/src/gui/plugins/component_inspector/ComponentInspector.cc b/src/gui/plugins/component_inspector/ComponentInspector.cc index cb29338b8d..042c94913e 100644 --- a/src/gui/plugins/component_inspector/ComponentInspector.cc +++ b/src/gui/plugins/component_inspector/ComponentInspector.cc @@ -1054,7 +1054,7 @@ Entity ComponentInspector::GetEntity() const } ///////////////////////////////////////////////// -void ComponentInspector::SetEntity(const Entity &_entity) +void ComponentInspector::SetEntity(const gazebo::Entity &_entity) { // If nothing is selected, display world properties if (_entity == kNullEntity) diff --git a/src/gui/plugins/component_inspector/ComponentInspector.hh b/src/gui/plugins/component_inspector/ComponentInspector.hh index 1c23dddf1a..0686a89d04 100644 --- a/src/gui/plugins/component_inspector/ComponentInspector.hh +++ b/src/gui/plugins/component_inspector/ComponentInspector.hh @@ -346,7 +346,7 @@ namespace gazebo /// \brief Set the entity currently inspected. /// \param[in] _entity Entity ID. - public: Q_INVOKABLE void SetEntity(const Entity &_entity); + public: Q_INVOKABLE void SetEntity(const gazebo::Entity &_entity); /// \brief Notify that entity has changed. signals: void EntityChanged(); diff --git a/src/gui/plugins/component_inspector/ComponentInspector.qml b/src/gui/plugins/component_inspector/ComponentInspector.qml index 9e25fb8c93..39038d65d3 100644 --- a/src/gui/plugins/component_inspector/ComponentInspector.qml +++ b/src/gui/plugins/component_inspector/ComponentInspector.qml @@ -306,7 +306,7 @@ Rectangle { } ToolButton { - id: addButton + id: addLinkButton checkable: false text: "Add entity" visible: entityType == "model" @@ -314,26 +314,26 @@ Rectangle { fillMode: Image.Pad horizontalAlignment: Image.AlignHCenter verticalAlignment: Image.AlignVCenter - source: "qrc:/Gazebo/images/plus.png" + source: "qrc:/Gazebo/images/plus-link.png" sourceSize.width: 18; sourceSize.height: 18; } - ToolTip.text: "Add an entity to a model" + ToolTip.text: "Add a link or light to a model" ToolTip.visible: hovered ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval onClicked: { - getSimPaused() ? addLinkMenu.open() : pausePopup.open() + getSimPaused() ? addLinkMenu.open() : linkAddPausePopup.open() } Popup { - id: pausePopup + id: linkAddPausePopup modal: true focus: true - x: parent.width - popupContentText.width - y: parent.height + popupContentText.height + x: parent.width - linkAdPopupContentText.width + y: parent.height + linkAdPopupContentText.height contentItem: Text { - id: popupContentText + id: linkAdPopupContentText padding: 10 - text: "Pause simulation to add an entity" + text: "Pause simulation to add a link or light" } closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent } @@ -350,7 +350,6 @@ Rectangle { } } - Menu { id: addLinkMenu @@ -577,7 +576,175 @@ Rectangle { addLinkMenu.close() } } + } + } + ToolButton { + id: addSensorButton + checkable: false + text: "Add sensor" + visible: entityType == "link" + contentItem: Image { + fillMode: Image.Pad + horizontalAlignment: Image.AlignHCenter + verticalAlignment: Image.AlignVCenter + source: "qrc:/Gazebo/images/plus-sensor.png" + sourceSize.width: 18; + sourceSize.height: 18; + } + ToolTip.text: "Add a sensor to a link" + ToolTip.visible: hovered + ToolTip.delay: Qt.styleHints.mousePressAndHoldInterval + onClicked: { + getSimPaused() ? addSensorMenu.open() : sensorAddPausePopup.open() + } + Popup { + id: sensorAddPausePopup + modal: true + focus: true + x: parent.width - sensorAddPopupContentText.width + y: parent.height + sensorAddPopupContentText.height + contentItem: Text { + id: sensorAddPopupContentText + padding: 10 + text: "Pause simulation to add a sensor" + } + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + } + + Menu { + id: addSensorMenu + MenuItem { + id: airPressure + text: "Air pressure" + onTriggered: { + ComponentInspector.OnAddEntity(airPressure.text, "sensor"); + } + } + + MenuItem { + id: altimeter + text: "Altimeter" + onTriggered: { + ComponentInspector.OnAddEntity(altimeter.text, "sensor"); + } + } + + MenuItem { + id: cameraSensorMenu + text: "Camera >" + + MouseArea { + id: viewSubCameraArea + anchors.fill: parent + hoverEnabled: true + onEntered: cameraSubmenu.open() + } + } + + MenuItem { + id: contact + text: "Contact" + onTriggered: { + ComponentInspector.OnAddEntity(contact.text, "sensor"); + } + } + + MenuItem { + id: forceTorque + text: "Force torque" + onTriggered: { + ComponentInspector.OnAddEntity(forceTorque.text, "sensor"); + } + } + + /*MenuItem { + id: gps + text: "GPS" + onTriggered: { + ComponentInspector.OnAddEntity(gps.text, "sensor"); + } + }*/ + + MenuItem { + id: gpuLidar + text: "GPU Lidar" + onTriggered: { + ComponentInspector.OnAddEntity("gpu_lidar", "sensor"); + } + } + + MenuItem { + id: imu + text: "IMU" + onTriggered: { + ComponentInspector.OnAddEntity(imu.text, "sensor"); + } + } + + MenuItem { + id: magnetometer + text: "Magnetometer" + onTriggered: { + ComponentInspector.OnAddEntity(magnetometer.text, "sensor"); + } + } + } + + Menu { + id: cameraSubmenu + x: addSensorMenu.x - addSensorMenu.width + y: addSensorMenu.y + cameraSensorMenu.y + + MenuItem { + id: depth + text: "Depth" + onTriggered: { + ComponentInspector.OnAddEntity("depth_camera", "sensor"); + } + } + MenuItem { + id: logical + text: "Logical" + onTriggered: { + ComponentInspector.OnAddEntity("logical_camera", "sensor"); + } + } + MenuItem { + id: monocular + text: "Monocular" + onTriggered: { + ComponentInspector.OnAddEntity("camera", "sensor"); + } + } + /*MenuItem { + id: multicamera + text: "Multicamera" + onTriggered: { + ComponentInspector.OnAddEntity("multicamera", "sensor"); + } + }*/ + MenuItem { + id: rgbd + text: "RGBD" + onTriggered: { + ComponentInspector.OnAddEntity("rgbd_camera", "sensor"); + } + } + MenuItem { + id: segmentation + text: "Segmentation" + onTriggered: { + ComponentInspector.OnAddEntity("segmentation_camera", "sensor"); + } + } + MenuItem { + id: thermal + text: "Thermal" + onTriggered: { + ComponentInspector.OnAddEntity("thermal_camera", "sensor"); + } + } } } @@ -592,7 +759,6 @@ Rectangle { } } - ListView { anchors.top: header.bottom anchors.bottom: parent.bottom diff --git a/src/gui/plugins/component_inspector/ExpandingTypeHeader.qml b/src/gui/plugins/component_inspector/ExpandingTypeHeader.qml index 7f8126801d..eb98b926fc 100644 --- a/src/gui/plugins/component_inspector/ExpandingTypeHeader.qml +++ b/src/gui/plugins/component_inspector/ExpandingTypeHeader.qml @@ -49,7 +49,7 @@ Rectangle { fillMode: Image.Pad Layout.alignment : Qt.AlignVCenter source: content.show ? - "qrc:/Gazebo/images/minus.png" : "qrc:/Gazebo/images/plus.png" + "qrc:/Gazebo/images/chevron-down.svg" : "qrc:/Gazebo/images/chevron-right.svg" } TypeHeader { id: typeHeader diff --git a/src/gui/plugins/component_inspector/Imu.cc b/src/gui/plugins/component_inspector/Imu.cc index 6949f9ba1d..18e4969493 100644 --- a/src/gui/plugins/component_inspector/Imu.cc +++ b/src/gui/plugins/component_inspector/Imu.cc @@ -103,7 +103,7 @@ Q_INVOKABLE void Imu::OnLinearAccelerationXNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); @@ -137,7 +137,7 @@ Q_INVOKABLE void Imu::OnLinearAccelerationYNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); @@ -171,7 +171,7 @@ Q_INVOKABLE void Imu::OnLinearAccelerationZNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); @@ -205,7 +205,7 @@ Q_INVOKABLE void Imu::OnAngularVelocityXNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); @@ -239,7 +239,7 @@ Q_INVOKABLE void Imu::OnAngularVelocityYNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); @@ -273,7 +273,7 @@ Q_INVOKABLE void Imu::OnAngularVelocityZNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Imu *imu = comp->Data().ImuSensor(); diff --git a/src/gui/plugins/component_inspector/JointType.cc b/src/gui/plugins/component_inspector/JointType.cc index ffcc757f3e..63d9dfe5d6 100644 --- a/src/gui/plugins/component_inspector/JointType.cc +++ b/src/gui/plugins/component_inspector/JointType.cc @@ -81,10 +81,10 @@ Q_INVOKABLE void JointType::OnJointType(QString _jointType) [=](EntityComponentManager &_ecm) { components::JointType *comp = - _ecm.Component(this->inspector->Entity()); + _ecm.Component(this->inspector->GetEntity()); components::ParentEntity *parentComp = - _ecm.Component(this->inspector->Entity()); + _ecm.Component(this->inspector->GetEntity()); if (comp && parentComp) { diff --git a/src/gui/plugins/component_inspector/Lidar.cc b/src/gui/plugins/component_inspector/Lidar.cc index 48ad47b2fc..8fbf6e95c1 100644 --- a/src/gui/plugins/component_inspector/Lidar.cc +++ b/src/gui/plugins/component_inspector/Lidar.cc @@ -82,7 +82,7 @@ Q_INVOKABLE void Lidar::OnLidarNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Lidar *lidar = comp->Data().LidarSensor(); @@ -120,7 +120,7 @@ Q_INVOKABLE void Lidar::OnLidarChange( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Lidar *lidar = comp->Data().LidarSensor(); diff --git a/src/gui/plugins/component_inspector/Magnetometer.cc b/src/gui/plugins/component_inspector/Magnetometer.cc index c6f2ca59be..3c789582bc 100644 --- a/src/gui/plugins/component_inspector/Magnetometer.cc +++ b/src/gui/plugins/component_inspector/Magnetometer.cc @@ -81,7 +81,7 @@ Q_INVOKABLE void Magnetometer::OnMagnetometerXNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Magnetometer *mag = comp->Data().MagnetometerSensor(); @@ -115,7 +115,7 @@ Q_INVOKABLE void Magnetometer::OnMagnetometerYNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Magnetometer *mag = comp->Data().MagnetometerSensor(); @@ -149,7 +149,7 @@ Q_INVOKABLE void Magnetometer::OnMagnetometerZNoise( [=](EntityComponentManager &_ecm) { auto comp = _ecm.Component( - this->inspector->Entity()); + this->inspector->GetEntity()); if (comp) { sdf::Magnetometer *mag = comp->Data().MagnetometerSensor(); diff --git a/src/gui/plugins/component_inspector/ModelEditor.cc b/src/gui/plugins/component_inspector/ModelEditor.cc index c6547cac73..e5ebb018a6 100644 --- a/src/gui/plugins/component_inspector/ModelEditor.cc +++ b/src/gui/plugins/component_inspector/ModelEditor.cc @@ -29,7 +29,14 @@ #include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include "ignition/gazebo/components/Model.hh" @@ -66,27 +73,39 @@ namespace ignition::gazebo /// \param[in] _geomOrLightType Geometry or light type, e.g. sphere, /// directional, etc /// \param[in] _entityType Type of entity: link, visual, collision, etc - /// \param[in] _parentEntity Name of parent entity + /// \param[in] _parentEntity Parent entity /// \param[in] _data Additional variable data for specific instances public: void HandleAddEntity(const std::string &_geomOrLightType, const std::string &_entityType, Entity _parentEntity, const std::unordered_map &_data); - /// \brief Get a SDF string of a geometry + /// \brief Create a geom /// \param[in] _eta Entity to add. - public: std::string GeomSDFString(const EntityToAdd &_eta) const; + public: std::optional CreateGeom( + const EntityToAdd &_eta) const; - /// \brief Get a SDF string of a light + /// \brief Create a light /// \param[in] _eta Entity to add. - public: std::string LightSDFString(const EntityToAdd &_eta) const; + public: std::optional CreateLight( + const EntityToAdd &_eta) const; - /// \brief Get a SDF string of a link + /// \brief Create a link /// \param[in] _eta Entity to add. - public: std::string LinkSDFString(const EntityToAdd &_eta) const; + public: std::optional CreateLink( + const EntityToAdd &_eta, + EntityComponentManager &_ecm) const; - /// \brief Get a SDF string of a link + /// \brief Create a sensor /// \param[in] _eta Entity to add. - public: sdf::ElementPtr JointSDF(const EntityToAdd &_eta) const; + public: std::optional CreateSensor( + const EntityToAdd &_eta, + EntityComponentManager &_ecm) const; + + /// \brief Create a joint + /// \param[in] _eta Entity to add. + public: std::optional CreateJoint( + const EntityToAdd &_eta, + EntityComponentManager &_ecm) const; /// \brief Entity Creator API. public: std::unique_ptr entityCreator{nullptr}; @@ -102,6 +121,9 @@ namespace ignition::gazebo // public: std::vector> linksToAdd; public: std::vector entitiesToAdd; + /// \brief Sensors to add to the ECM + public: std::vector sensorsToAdd; + /// \brief Event Manager public: EventManager eventMgr; }; @@ -153,85 +175,45 @@ void ModelEditor::Update(const UpdateInfo &, if (eta.entityType == "link") { - sdf::Link linkSdf; - // create an sdf::Link to it can be added to the ECM throught the - // CreateEntities call - std::string linkSDFStr = this->dataPtr->LinkSDFString(eta); - if (!linkSDFStr.empty()) + std::optional link = this->dataPtr->CreateLink(eta, _ecm); + if (link) { - linkSDFStr = std::string("" + - linkSDFStr + ""; - - sdf::ElementPtr linkElem(new sdf::Element); - sdf::initFile("link.sdf", linkElem); - sdf::readString(linkSDFStr, linkElem); - linkSdf.Load(linkElem); + Entity entity = this->dataPtr->entityCreator->CreateEntities(&(*link)); + this->dataPtr->entityCreator->SetParent(entity, eta.parentEntity); + // Make sure to mark the parent as needing recreation. This will + // tell the server to rebuild the model with the new link. + _ecm.CreateComponent(eta.parentEntity, components::Recreate()); + entities.push_back(entity); } - else - { - continue; - } - - // generate unique link name - // note passing components::Link() as arg to EntityByComponents causes - // a crash on exit, see issue #1158 - std::string linkName = "link"; - Entity linkEnt = _ecm.EntityByComponents( - components::ParentEntity(eta.parentEntity), - components::Name(linkName)); - int64_t counter = 0; - while (linkEnt) + } + else if (eta.entityType == "sensor") + { + std::optional sensor = + this->dataPtr->CreateSensor(eta, _ecm); + if (sensor) { - linkName = std::string("link") + "_" + std::to_string(++counter); - linkEnt = _ecm.EntityByComponents( - components::ParentEntity(eta.parentEntity), - components::Name(linkName)); + Entity entity = this->dataPtr->entityCreator->CreateEntities( + &(*sensor)); + this->dataPtr->entityCreator->SetParent(entity, eta.parentEntity); + // Make sure to mark the parent as needing recreation. This will + // tell the server to rebuild the model with the new link. + _ecm.CreateComponent(eta.parentEntity, components::Recreate()); + entities.push_back(entity); } - - linkSdf.SetName(linkName); - auto entity = this->dataPtr->entityCreator->CreateEntities(&linkSdf); - this->dataPtr->entityCreator->SetParent(entity, eta.parentEntity); - // Make sure to mark the parent as needing recreation. This will - // tell the server to rebuild the model with the new link. - _ecm.CreateComponent(eta.parentEntity, components::Recreate()); - - // traverse the tree and add all new entities created by the entity - // creator to the set - entities.push_back(entity); } else if (eta.entityType == "joint") { - sdf::ElementPtr jointElem = this->dataPtr->JointSDF(eta); - if (nullptr == jointElem) - continue; - - std::string jointName = "joint"; - Entity jointEnt = _ecm.EntityByComponents( - components::ParentEntity(eta.parentEntity), - components::Name(jointName)); - int64_t counter = 0; - while (jointEnt) + std::optional joint = this->dataPtr->CreateJoint(eta, _ecm); + if (joint) { - jointName = std::string("joint") + "_" + std::to_string(++counter); - jointEnt = _ecm.EntityByComponents( - components::ParentEntity(eta.parentEntity), - components::Name(jointName)); + Entity entity = this->dataPtr->entityCreator->CreateEntities( + &(*joint), true); + this->dataPtr->entityCreator->SetParent(entity, eta.parentEntity); + // Make sure to mark the parent as needing recreation. This will + // tell the server to rebuild the model with the new link. + _ecm.CreateComponent(eta.parentEntity, components::Recreate()); + entities.push_back(entity); } - - jointElem->SetName(jointName); - - sdf::Joint jointSdf; - jointSdf.Load(jointElem); - auto entity = - this->dataPtr->entityCreator->CreateEntities(&jointSdf, true); - this->dataPtr->entityCreator->SetParent(entity, eta.parentEntity); - // Make sure to mark the parent as needing recreation. This will - // tell the server to rebuild the model with the new link. - _ecm.CreateComponent(eta.parentEntity, components::Recreate()); - - // traverse the tree and add all new entities created by the entity - // creator to the set - entities.push_back(entity); } } @@ -251,10 +233,10 @@ void ModelEditor::Update(const UpdateInfo &, entities.push_back(child); } - // use tmp AddedRemovedEntities event to update other gui plugins + // use GuiNewRemovedEntities event to update other gui plugins // note this event will be removed in Ignition Garden std::set removedEntities; - ignition::gazebo::gui::events::AddedRemovedEntities event( + ignition::gazebo::gui::events::GuiNewRemovedEntities event( newEntities, removedEntities); ignition::gui::App()->sendEvent( ignition::gui::App()->findChild(), @@ -289,186 +271,258 @@ bool ModelEditor::eventFilter(QObject *_obj, QEvent *_event) } ///////////////////////////////////////////////// -std::string ModelEditorPrivate::LightSDFString(const EntityToAdd &_eta) const +std::optional ModelEditorPrivate::CreateLight( + const EntityToAdd &_eta) const { - std::stringstream lightStr; - lightStr << ""; + sdf::Light light; + light.SetCastShadows(false); + light.SetDiffuse(math::Color(1, 1, 1, 1)); + light.SetSpecular(math::Color(0.5, 0.5, 0.5, 0.5)); if (_eta.geomOrLightType == "directional") { - lightStr - << "false" - << "1.0 1.0 1.0 1" - << "0.5 0.5 0.5 1"; + light.SetType(sdf::LightType::DIRECTIONAL); } - else if (_eta.geomOrLightType == "spot") + else if (_eta.geomOrLightType == "spot" || _eta.geomOrLightType == "point") { - lightStr - << "false" - << "1.0 1.0 1.0 1" - << "0.5 0.5 0.5 1" - << "" - << "4" - << "0.2" - << "0.5" - << "0.01" - << "" - << "0 0 -1" - << "" - << "0.1" - << "0.5" - << "0.8" - << ""; - } - else if (_eta.geomOrLightType == "point") - { - lightStr - << "false" - << "1.0 1.0 1.0 1" - << "0.5 0.5 0.5 1" - << "" - << "4" - << "0.2" - << "0.5" - << "0.01" - << ""; + light.SetType(sdf::LightType::SPOT); + light.SetAttenuationRange(4); + light.SetConstantAttenuationFactor(0.2); + light.SetLinearAttenuationFactor(0.5); + light.SetQuadraticAttenuationFactor(0.01); + + if (_eta.geomOrLightType == "spot") + { + light.SetSpotInnerAngle(0.1); + light.SetSpotOuterAngle(0.5); + light.SetSpotFalloff(0.8); + } } else { ignwarn << "Light type not supported: " << _eta.geomOrLightType << std::endl; - return std::string(); + return std::nullopt; } - lightStr << ""; - return lightStr.str(); + return light; } - ///////////////////////////////////////////////// -std::string ModelEditorPrivate::GeomSDFString(const EntityToAdd &_eta) const +std::optional ModelEditorPrivate::CreateGeom( + const EntityToAdd &_eta) const { math::Vector3d size = math::Vector3d::One; - std::stringstream geomStr; - geomStr << ""; + sdf::Geometry geom; + if (_eta.geomOrLightType == "box") { - geomStr - << "" - << " " << size << "" - << ""; + sdf::Box shape; + shape.SetSize(size); + geom.SetBoxShape(shape); + geom.SetType(sdf::GeometryType::BOX); } else if (_eta.geomOrLightType == "sphere") { - geomStr - << "" - << " " << size.X() * 0.5 << "" - << ""; + sdf::Sphere shape; + shape.SetRadius(size.X() * 0.5); + geom.SetSphereShape(shape); + geom.SetType(sdf::GeometryType::SPHERE); } else if (_eta.geomOrLightType == "cylinder") { - geomStr - << "" - << " " << size.X() * 0.5 << "" - << " " << size.Z() << "" - << ""; + sdf::Cylinder shape; + shape.SetRadius(size.X() * 0.5); + shape.SetLength(size.Z()); + geom.SetCylinderShape(shape); + geom.SetType(sdf::GeometryType::CYLINDER); } else if (_eta.geomOrLightType == "capsule") { - geomStr - << "" - << " " << size.X() * 0.5 << "" - << " " << size.Z() << "" - << ""; + sdf::Capsule shape; + shape.SetRadius(size.X() * 0.5); + shape.SetLength(size.Z()); + geom.SetCapsuleShape(shape); + geom.SetType(sdf::GeometryType::CAPSULE); } else if (_eta.geomOrLightType == "ellipsoid") { - geomStr - << "" - << " " << size * 0.5 << "" - << ""; + sdf::Ellipsoid shape; + shape.SetRadii(size * 0.5); + geom.SetEllipsoidShape(shape); + geom.SetType(sdf::GeometryType::ELLIPSOID); } else if (_eta.geomOrLightType == "mesh") { - geomStr - << "" - << " " << _eta.data.at("uri") << "" - << ""; + sdf::Mesh shape; + shape.SetUri(_eta.data.at("uri")); + geom.SetMeshShape(shape); + geom.SetType(sdf::GeometryType::MESH); } else { ignwarn << "Geometry type not supported: " << _eta.geomOrLightType << std::endl; - return std::string(); + return std::nullopt; } - - geomStr << ""; - return geomStr.str(); + return geom; } ///////////////////////////////////////////////// -sdf::ElementPtr ModelEditorPrivate::JointSDF(const EntityToAdd &_eta) const +std::optional ModelEditorPrivate::CreateLink( + const EntityToAdd &_eta, EntityComponentManager &_ecm) const { - std::unordered_set validJointTypes = { - "revolute", "ball", "continuous", "fixed", "gearbox", "prismatic", - "revolute2", "screw", "universal"}; - - if (validJointTypes.count(_eta.geomOrLightType) == 0) + sdf::Link link; + if (_eta.parentEntity == kNullEntity) { - ignwarn << "Joint type not supported: " - << _eta.geomOrLightType << std::endl; - return nullptr; + ignerr << "Parent entity not defined." << std::endl; + return std::nullopt; } - auto joint = std::make_shared(); - sdf::initFile("joint.sdf", joint); + if (_eta.geomOrLightType == "spot" || _eta.geomOrLightType == "directional" || + _eta.geomOrLightType == "point") + { + std::optional light = this->CreateLight(_eta); + if (light) + link.AddLight(*light); + } + else + { + std::optional geom = this->CreateGeom(_eta); + if (geom) + { + sdf::Collision collision; + collision.SetName("collision"); + collision.SetGeom(*geom); + link.AddCollision(collision); + + sdf::Visual visual; + visual.SetName("visual"); + visual.SetGeom(*geom); + link.AddVisual(visual); + } + } - joint->GetAttribute("name")->Set("joint"); - joint->GetAttribute("type")->Set(_eta.geomOrLightType); - joint->GetElement("parent")->Set(_eta.data.at("parent_link")); - joint->GetElement("child")->Set(_eta.data.at("child_link")); + // generate unique link name + // note passing components::Link() as arg to EntityByComponents causes + // a crash on exit, see issue #1158 + std::string linkName = "link"; + Entity linkEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(linkName)); + int64_t counter = 0; + while (linkEnt) + { + linkName = std::string("link") + "_" + std::to_string(++counter); + linkEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(linkName)); + } - return joint; + link.SetName(linkName); + return link; } ///////////////////////////////////////////////// -std::string ModelEditorPrivate::LinkSDFString(const EntityToAdd &_eta) const +std::optional ModelEditorPrivate::CreateSensor( + const EntityToAdd &_eta, EntityComponentManager &_ecm) const { - std::stringstream linkStr; - if (_eta.geomOrLightType == "empty") + // Exit early if there is no parent entity + if (_eta.parentEntity == kNullEntity) { - linkStr << ""; - return linkStr.str(); + ignerr << "Parent entity not defined." << std::endl; + return std::nullopt; } - std::string geomOrLightStr; - if (_eta.geomOrLightType == "spot" || _eta.geomOrLightType == "directional" || - _eta.geomOrLightType == "point") + sdf::Sensor sensor; + + std::string type; + + // Replace spaces with underscores. + common::replaceAll(type, _eta.geomOrLightType, " ", "_"); + + std::ostringstream stream; + stream << "" + << "" + << "<" << type << ">"; + + auto sdfStr = stream.str(); + sdf::ElementPtr sensorElem(new sdf::Element); + sdf::initFile("sensor.sdf", sensorElem); + sdf::readString(sdfStr, sensorElem); + sensor.Load(sensorElem); + + // generate unique sensor name + // note passing components::Link() as arg to EntityByComponents causes + // a crash on exit, see issue #1158 + std::string sensorName = type; + Entity sensorEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(sensorName)); + int64_t counter = 0; + while (sensorEnt) { - geomOrLightStr = this->LightSDFString(_eta); - linkStr - << "" - << geomOrLightStr - << ""; + sensorName = type + "_" + std::to_string(++counter); + sensorEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(sensorName)); } + sensor.SetName(sensorName); + + sensor.SetTopic("/" + sensorName); + + return sensor; +} + +///////////////////////////////////////////////// +std::optional ModelEditorPrivate::CreateJoint( + const EntityToAdd &_eta, EntityComponentManager &_ecm) const +{ + sdf::Joint joint; + + if (_eta.geomOrLightType == "ball") + joint.SetType(sdf::JointType::BALL); + else if (_eta.geomOrLightType == "continuous") + joint.SetType(sdf::JointType::CONTINUOUS); + else if (_eta.geomOrLightType == "fixed") + joint.SetType(sdf::JointType::FIXED); + else if (_eta.geomOrLightType == "gearbox") + joint.SetType(sdf::JointType::GEARBOX); + else if (_eta.geomOrLightType == "prismatic") + joint.SetType(sdf::JointType::PRISMATIC); + else if (_eta.geomOrLightType == "revolute") + joint.SetType(sdf::JointType::REVOLUTE); + else if (_eta.geomOrLightType == "revolute2") + joint.SetType(sdf::JointType::REVOLUTE2); + else if (_eta.geomOrLightType == "screw") + joint.SetType(sdf::JointType::SCREW); else { - geomOrLightStr = this->GeomSDFString(_eta); - linkStr - << "" - << " " - << geomOrLightStr - << " " - << " " - << geomOrLightStr - << " " - << ""; + ignwarn << "Joint type not supported: " + << _eta.geomOrLightType << std::endl; + + return std::nullopt; + } + + joint.SetParentLinkName(_eta.data.at("parent_link")); + joint.SetChildLinkName(_eta.data.at("child_link")); + + std::string jointName = "joint"; + Entity jointEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(jointName)); + int64_t counter = 0; + while (jointEnt) + { + jointName = std::string("joint") + "_" + std::to_string(++counter); + jointEnt = _ecm.EntityByComponents( + components::ParentEntity(_eta.parentEntity), + components::Name(jointName)); } - if (geomOrLightStr.empty()) - return std::string(); + joint.SetName(jointName); - return linkStr.str(); + return joint; } ///////////////////////////////////////////////// diff --git a/src/gui/plugins/entity_tree/EntityTree.qml b/src/gui/plugins/entity_tree/EntityTree.qml index ae1d8fec19..c96c5d78a8 100644 --- a/src/gui/plugins/entity_tree/EntityTree.qml +++ b/src/gui/plugins/entity_tree/EntityTree.qml @@ -50,6 +50,14 @@ Rectangle { Material.color(Material.Grey, Material.Shade100) : Material.color(Material.Grey, Material.Shade800) + /** + * Highlight color + */ + property color highlightColor: Qt.rgba( + Material.accent.r, + Material.accent.g, + Material.accent.b, 0.3) + /** * Height of each item in pixels */ @@ -316,7 +324,7 @@ Rectangle { branchDelegate: Rectangle { height: itemHeight width: itemHeight * 0.75 - color: lightGrey + color: "transparent" Image { id: icon sourceSize.height: itemHeight * 0.4 @@ -325,7 +333,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right source: styleData.isExpanded ? - "qrc:/Gazebo/images/minus.png" : "qrc:/Gazebo/images/plus.png" + "qrc:/Gazebo/images/chevron-down.svg" : "qrc:/Gazebo/images/chevron-right.svg" } MouseArea { anchors.fill: parent @@ -347,7 +355,7 @@ Rectangle { rowDelegate: Rectangle { visible: styleData.row !== undefined height: itemHeight - color: styleData.selected ? Material.accent : (styleData.row % 2 == 0) ? even : odd + color: styleData.selected ? highlightColor : (styleData.row % 2 == 0) ? even : odd MouseArea { anchors.fill: parent hoverEnabled: true @@ -366,7 +374,7 @@ Rectangle { itemDelegate: Rectangle { id: itemDel - color: styleData.selected ? Material.accent : (styleData.row % 2 == 0) ? even : odd + color: "transparent" height: itemHeight diff --git a/src/gui/plugins/scene_manager/GzSceneManager.cc b/src/gui/plugins/scene_manager/GzSceneManager.cc index 6014d14b9d..cccb6caaa4 100644 --- a/src/gui/plugins/scene_manager/GzSceneManager.cc +++ b/src/gui/plugins/scene_manager/GzSceneManager.cc @@ -18,8 +18,6 @@ #include "GzSceneManager.hh" -#include - #include #include #include @@ -136,11 +134,11 @@ bool GzSceneManager::eventFilter(QObject *_obj, QEvent *_event) this->dataPtr->OnRender(); } else if (_event->type() == - ignition::gazebo::gui::events::AddedRemovedEntities::kType) + ignition::gazebo::gui::events::GuiNewRemovedEntities::kType) { std::lock_guard lock(this->dataPtr->newRemovedEntityMutex); auto addedRemovedEvent = - reinterpret_cast(_event); + reinterpret_cast(_event); if (addedRemovedEvent) { for (auto entity : addedRemovedEvent->NewEntities()) diff --git a/src/gui/resources/gazebo.qrc b/src/gui/resources/gazebo.qrc index 0845b98594..96fa71b8ff 100644 --- a/src/gui/resources/gazebo.qrc +++ b/src/gui/resources/gazebo.qrc @@ -2,6 +2,8 @@ GazeboDrawer.qml images/actor.png + images/chevron-down.svg + images/chevron-right.svg images/collision.png images/joint.png images/light.png @@ -10,6 +12,8 @@ images/minus.png images/model.png images/plus.png + images/plus-link.png + images/plus-sensor.png images/sensor.png images/unlock.svg images/visual.png diff --git a/src/gui/resources/images/chevron-down.svg b/src/gui/resources/images/chevron-down.svg new file mode 100644 index 0000000000..4e51b0d257 --- /dev/null +++ b/src/gui/resources/images/chevron-down.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/gui/resources/images/chevron-right.svg b/src/gui/resources/images/chevron-right.svg new file mode 100644 index 0000000000..9f1b78b943 --- /dev/null +++ b/src/gui/resources/images/chevron-right.svg @@ -0,0 +1,66 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/src/gui/resources/images/plus-link.png b/src/gui/resources/images/plus-link.png new file mode 100644 index 0000000000..527fd36d09 Binary files /dev/null and b/src/gui/resources/images/plus-link.png differ diff --git a/src/gui/resources/images/plus-sensor.png b/src/gui/resources/images/plus-sensor.png new file mode 100644 index 0000000000..2053152b41 Binary files /dev/null and b/src/gui/resources/images/plus-sensor.png differ