diff --git a/.github/ci/packages.apt b/.github/ci/packages.apt
index efc76290ed..740b9f9d7d 100644
--- a/.github/ci/packages.apt
+++ b/.github/ci/packages.apt
@@ -15,7 +15,7 @@ libignition-tools-dev
libignition-transport12-dev
libignition-utils1-cli-dev
libogre-1.9-dev
-libogre-2.1-dev
+libogre-2.2-dev
libprotobuf-dev
libprotoc-dev
libsdformat13-dev
diff --git a/Changelog.md b/Changelog.md
index 2cfb1cc5d9..6d013fda34 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -4,6 +4,103 @@
## Ignition Gazebo 6.x
+### Ignition Gazebo 6.8.0 (2022-04-04)
+
+1. ServerConfig accepts an sdf::Root DOM object
+ * [Pull request #1333](https://github.com/ignitionrobotics/ign-gazebo/pull/1333)
+
+1. Disable sensors in sensors system when battery is drained
+ * [Pull request #1385](https://github.com/ignitionrobotics/ign-gazebo/pull/1385)
+
+1. Referring to Fuel assets within a heightmap
+ * [Pull request #1419](https://github.com/ignitionrobotics/ign-gazebo/pull/1419)
+
+1. Add the Model Photo Shoot system, port of Modelpropshop plugin from Gazebo classic
+ * [Pull request #1331](https://github.com/ignitionrobotics/ign-gazebo/pull/1331)
+
+1. Distortion camera integration test
+ * [Pull request #1374](https://github.com/ignitionrobotics/ign-gazebo/pull/1374)
+
+1. Add wheel slip user command
+ * [Pull request #1241](https://github.com/ignitionrobotics/ign-gazebo/pull/1241)
+
+1. SceneBroadcaster: only send changed state information for change events
+ * [Pull request #1392](https://github.com/ignitionrobotics/ign-gazebo/pull/1392)
+
+1. Fortress: Install Ogre 2.2, simplify docker
+ * [Pull request #1395](https://github.com/ignitionrobotics/ign-gazebo/pull/1395)
+
+1. Disable tests that are expected to fail on Windows
+ * [Pull request #1408](https://github.com/ignitionrobotics/ign-gazebo/pull/1408)
+
+1. Added user command to set multiple entities
+ * [Pull request #1394](https://github.com/ignitionrobotics/ign-gazebo/pull/1394)
+
+1. Fix JointStatePublisher topic name for nested models
+ * [Pull request #1405](https://github.com/ignitionrobotics/ign-gazebo/pull/1405)
+
+1. add initial_position param to joint controller system
+ * [Pull request #1406](https://github.com/ignitionrobotics/ign-gazebo/pull/1406)
+
+1. Component inspector: refactor Pose3d C++ code into a separate class
+ * [Pull request #1400](https://github.com/ignitionrobotics/ign-gazebo/pull/1400)
+
+1. Prevent hanging when world has only non-world plugins
+ * [Pull request #1383](https://github.com/ignitionrobotics/ign-gazebo/pull/1383)
+
+1. Toggle Light visuals
+ * [Pull request #1387](https://github.com/ignitionrobotics/ign-gazebo/pull/1387)
+
+1. Disable PeerTracker.PeerTrackerStale on macOS
+ * [Pull request #1398](https://github.com/ignitionrobotics/ign-gazebo/pull/1398)
+
+1. Disable ModelCommandAPI_TEST.RgbdCameraSensor on macOS
+ * [Pull request #1397](https://github.com/ignitionrobotics/ign-gazebo/pull/1397)
+
+1. Don't mark entities with a ComponentState::NoChange component as modified
+ * [Pull request #1391](https://github.com/ignitionrobotics/ign-gazebo/pull/1391)
+
+1. Add gazebo Entity id to rendering sensor's user data
+ * [Pull request #1381](https://github.com/ignitionrobotics/ign-gazebo/pull/1381)
+
+1. Allow to turn on/off lights
+ * [Pull request #1343](https://github.com/ignitionrobotics/ign-gazebo/pull/1343)
+
+1. Added headless rendering tutorial
+ * [Pull request #1386](https://github.com/ignitionrobotics/ign-gazebo/pull/1386)
+
+1. Add xyz and rpy offset to published odometry pose
+ * [Pull request #1341](https://github.com/ignitionrobotics/ign-gazebo/pull/1341)
+
+1. Fix visualization python tutorial
+ * [Pull request #1377](https://github.com/ignitionrobotics/ign-gazebo/pull/1377)
+
+1. Populate GUI plugins that are empty
+ * [Pull request #1375](https://github.com/ignitionrobotics/ign-gazebo/pull/1375)
+
+### Ignition Gazebo 6.7.0 (2022-02-24)
+
+1. Added Python interfaces to some Ignition Gazebo methods
+ * [Pull request #1219](https://github.com/ignitionrobotics/ign-gazebo/pull/1219)
+
+1. Use pose multiplication instead of addition
+ * [Pull request #1369](https://github.com/ignitionrobotics/ign-gazebo/pull/1369)
+
+1. Disables Failing Buoyancy Tests on Win32
+ * [Pull request #1368](https://github.com/ignitionrobotics/ign-gazebo/pull/1368)
+
+1. Extend ShaderParam system to support loading different shader languages
+ * [Pull request #1335](https://github.com/ignitionrobotics/ign-gazebo/pull/1335)
+
+1. Populate names of colliding entities in contact points message
+ * [Pull request #1351](https://github.com/ignitionrobotics/ign-gazebo/pull/1351)
+
+1. Refactor System functionality into SystemManager
+ * [Pull request #1340](https://github.com/ignitionrobotics/ign-gazebo/pull/1340)
+
+1. GzSceneManager: Prevent crash boom when inserted from menu
+ * [Pull request #1371](https://github.com/ignitionrobotics/ign-gazebo/pull/1371)
+
### Ignition Gazebo 6.6.0 (2022-02-24)
1. Fix accessing empty JointPosition component in lift drag plugin
@@ -537,7 +634,98 @@
## Ignition Gazebo 5.x
-### Ignition Gazebo 5.X.X (202X-XX-XX)
+### Ignition Gazebo 5.4.0 (2022-03-31)
+
+1. Add the Model Photo Shoot system, port of Modelpropshop plugin from Gazebo classic
+ * [Pull request #1331](https://github.com/ignitionrobotics/ign-gazebo/pull/1331)
+
+1. Add wheel slip user command
+ * [Pull request #1241](https://github.com/ignitionrobotics/ign-gazebo/pull/1241)
+
+1. Added user command to set multiple entity poses
+ * [Pull request #1394](https://github.com/ignitionrobotics/ign-gazebo/pull/1394)
+
+1. Component inspector: refactor Pose3d C++ code into a separate class
+ * [Pull request #1400](https://github.com/ignitionrobotics/ign-gazebo/pull/1400)
+
+1. Toggle Light visuals
+ * [Pull request #1387](https://github.com/ignitionrobotics/ign-gazebo/pull/1387)
+
+1. Allow to turn on/off lights
+ * [Pull request #1343](https://github.com/ignitionrobotics/ign-gazebo/pull/1343)
+
+1. Added more sensor properties to scene/info topic
+ * [Pull request #1344](https://github.com/ignitionrobotics/ign-gazebo/pull/1344)
+
+1. JointStatePublisher publish parent, child and axis data
+ * [Pull request #1345](https://github.com/ignitionrobotics/ign-gazebo/pull/1345)
+
+1. Fixed light GUI component inspector
+ * [Pull request #1337](https://github.com/ignitionrobotics/ign-gazebo/pull/1337)
+
+1. Fix `UNIT_SdfGenerator_TEST`
+ * [Pull request #1319](https://github.com/ignitionrobotics/ign-gazebo/pull/1319)
+
+1. Add elevator system
+ * [Pull request #535](https://github.com/ignitionrobotics/ign-gazebo/pull/535)
+
+1. Removed unused variables in shapes plugin
+ * [Pull request #1321](https://github.com/ignitionrobotics/ign-gazebo/pull/1321)
+
+1. Log an error if JointPositionController cannot find the joint. (citadel retarget)
+ * [Pull request #1314](https://github.com/ignitionrobotics/ign-gazebo/pull/1314)
+
+1. Buoyancy: fix center of volume's reference frame
+ * [Pull request #1302](https://github.com/ignitionrobotics/ign-gazebo/pull/1302)
+
+1. Remove EachNew calls from sensor PreUpdates
+ * [Pull request #1281](https://github.com/ignitionrobotics/ign-gazebo/pull/1281)
+
+1. Prevent GzScene3D 💥 if another scene is already loaded
+ * [Pull request #1294](https://github.com/ignitionrobotics/ign-gazebo/pull/1294)
+
+1. Cleanup update call for non-rendering sensors
+ * [Pull request #1282](https://github.com/ignitionrobotics/ign-gazebo/pull/1282)
+
+1. Documentation Error
+ * [Pull request #1285](https://github.com/ignitionrobotics/ign-gazebo/pull/1285)
+
+1. Min and max parameters for velocity, acceleration, and jerk apply to linear and angular separately.
+ * [Pull request #1229](https://github.com/ignitionrobotics/ign-gazebo/pull/1229)
+
+1. Add project() call to examples
+ * [Pull request #1274](https://github.com/ignitionrobotics/ign-gazebo/pull/1274)
+
+1. Implement `/server_control::stop`
+ * [Pull request #1240](https://github.com/ignitionrobotics/ign-gazebo/pull/1240)
+
+1. 👩🌾 Make depth camera tests more robust (#897)
+ * [Pull request #897) (#1257](https://github.com/ignitionrobotics/ign-gazebo/pull/897) (#1257)
+
+1. Support battery draining start via topics
+ * [Pull request #1255](https://github.com/ignitionrobotics/ign-gazebo/pull/1255)
+
+1. Make tests run as fast as possible
+ * [Pull request #1194](https://github.com/ignitionrobotics/ign-gazebo/pull/1194)
+ * [Pull request #1250](https://github.com/ignitionrobotics/ign-gazebo/pull/1250)
+
+1. Fix visualize lidar
+ * [Pull request #1224](https://github.com/ignitionrobotics/ign-gazebo/pull/1224)
+
+1. Skip failing Windows tests
+ * [Pull request #1205](https://github.com/ignitionrobotics/ign-gazebo/pull/1205)
+ * [Pull request #1204](https://github.com/ignitionrobotics/ign-gazebo/pull/1204)
+ * [Pull request #1259](https://github.com/ignitionrobotics/ign-gazebo/pull/1259)
+ * [Pull request #1408](https://github.com/ignitionrobotics/ign-gazebo/pull/1408)
+
+1. Configurable joint state publisher's topic
+ * [Pull request #1076](https://github.com/ignitionrobotics/ign-gazebo/pull/1076)
+
+1. Thruster plugin: add tests and velocity control
+ * [Pull request #1190](https://github.com/ignitionrobotics/ign-gazebo/pull/1190)
+
+1. Limit thruster system's input thrust cmd
+ * [Pull request #1318](https://github.com/ignitionrobotics/ign-gazebo/pull/1318)
### Ignition Gazebo 5.3.0 (2021-11-12)
diff --git a/docker/Dockerfile.base b/docker/Dockerfile.base
index e84976b01d..ac03a69978 100644
--- a/docker/Dockerfile.base
+++ b/docker/Dockerfile.base
@@ -11,10 +11,6 @@ RUN scripts/enable_ign_stable.sh
COPY docker/scripts/install_common_deps.sh scripts/install_common_deps.sh
RUN scripts/install_common_deps.sh
-# This is not strictly necessary, but makes things faster down the line.
-COPY docker/scripts/install_ign_deps.sh scripts/install_ign_deps.sh
-RUN scripts/install_ign_deps.sh
-
COPY docker/scripts/enable_gcc8.sh scripts/enable_gcc8.sh
RUN scripts/enable_gcc8.sh
diff --git a/docker/scripts/install_ign_deps.sh b/docker/scripts/install_ign_deps.sh
deleted file mode 100755
index 63948f5fb6..0000000000
--- a/docker/scripts/install_ign_deps.sh
+++ /dev/null
@@ -1,82 +0,0 @@
-#!/bin/bash
-
-set -o errexit
-set -o verbose
-
-sudo apt-get update
-
-# Things that are used all over the ign stack
-sudo apt-get install --no-install-recommends -y \
- doxygen \
- libbullet-dev \
- libtinyxml2-dev \
- libprotoc-dev libprotobuf-dev \
- protobuf-compiler \
- ruby-ronn \
- ruby-dev \
- swig \
- uuid-dev
-
-# ign-common dependencies
-sudo apt-get install --no-install-recommends -y \
- libavcodec-dev \
- libavdevice-dev \
- libavformat-dev \
- libavutil-dev \
- libfreeimage-dev \
- libgts-dev \
- libswscale-dev
-
-# ign-gui dependencies
-sudo apt-get install --no-install-recommends -y \
- qtbase5-dev \
- qtdeclarative5-dev \
- qtquickcontrols2-5-dev \
- qml-module-qtquick2 \
- qml-module-qtquick-controls \
- qml-module-qtquick-controls2 \
- qml-module-qtquick-dialogs \
- qml-module-qtquick-layouts \
- qml-module-qt-labs-folderlistmodel \
- qml-module-qt-labs-settings \
- qml-module-qtgraphicaleffects
-
-# ign-rendering dependencies
-sudo apt-get install --no-install-recommends -y \
- libogre-1.9-dev \
- libogre-2.1-dev \
- libglew-dev \
- libfreeimage-dev \
- freeglut3-dev \
- libxmu-dev \
- libxi-dev
-
-# ign-transport dependencies
-sudo apt-get install --no-install-recommends -y \
- libzmq3-dev \
- libsqlite3-dev
-
-# SDFormat dependencies
-sudo apt-get install --no-install-recommends -y \
- libtinyxml-dev libxml2-dev
-
-# ign-fuel_tools dependencies
-sudo apt-get install --no-install-recommends -y \
- libcurl4-openssl-dev libjsoncpp-dev libzip-dev curl libyaml-dev
-
-# ign-physics dependencies
-sudo apt-get install --no-install-recommends -y \
- libeigen3-dev \
- libdart-collision-ode-dev \
- libdart-dev \
- libdart-external-ikfast-dev \
- libdart-external-odelcpsolver-dev \
- libdart-utils-urdf-dev \
- libbenchmark-dev
-
-# ign-gazebo dependencies
-sudo apt-get install --no-install-recommends -y \
- qml-module-qtqml-models2
-
-sudo apt-get clean && sudo rm -rf /var/lib/apt/lists/*
-
diff --git a/examples/worlds/empty_gui.sdf b/examples/worlds/empty_gui.sdf
new file mode 100644
index 0000000000..84b5e172c4
--- /dev/null
+++ b/examples/worlds/empty_gui.sdf
@@ -0,0 +1,210 @@
+
+
+
+
+
+
+
+
+
+
+ 3D View
+ false
+ docked
+
+
+ ogre2
+ scene
+ 0.4 0.4 0.4
+ 0.8 0.8 0.8
+ -6 0 6 0 0.5 0
+
+
+
+
+ 1.0 1.0 1.0
+ 0.8 0.8 0.8
+
+
+
+
+ true
+ 0 0 10 0 0 0
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.0 0.6 -0.9
+
+
+
+ true
+
+
+
+
+ 0 0 1
+
+
+
+
+
+
+ 0 0 1
+ 100 100
+
+
+
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+ 0.8 0.8 0.8 1
+
+
+
+
+
+
+ 0 0 0.5 0 0 0
+
+
+
+ 1
+ 0
+ 0
+ 1
+ 0
+ 1
+
+ 1.0
+
+
+
+
+ 1 1 1
+
+
+
+
+
+
+
+ 1 1 1
+
+
+
+ 1 0 0 1
+ 1 0 0 1
+ 1 0 0 1
+
+
+
+
+
+
+ 0 -1.5 0.5 0 0 0
+
+
+
+ 2
+ 0
+ 0
+ 2
+ 0
+ 2
+
+ 2.0
+
+
+
+
+ 0.5
+ 1.0
+
+
+
+
+
+
+
+ 0.5
+ 1.0
+
+
+
+ 0 1 0 1
+ 0 1 0 1
+ 0 1 0 1
+
+
+
+
+
+
+ 0 1.5 0.5 0 0 0
+
+
+
+ 3
+ 0
+ 0
+ 3
+ 0
+ 3
+
+ 3.0
+
+
+
+
+ 0.5
+
+
+
+
+
+
+
+ 0.5
+
+
+
+ 0 0 1 1
+ 0 0 1 1
+ 0 0 1 1
+
+
+
+
+
+
+
diff --git a/examples/worlds/fuel.sdf b/examples/worlds/fuel.sdf
index f18d586932..4d50302e1a 100644
--- a/examples/worlds/fuel.sdf
+++ b/examples/worlds/fuel.sdf
@@ -107,5 +107,54 @@
+
+
+ true
+
+
+
+
+ false
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/dirt_diffusespecular.png
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/flat_normal.png
+ 10
+
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/grass_diffusespecular.png
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/flat_normal.png
+ 10
+
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/fungus_diffusespecular.png
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/flat_normal.png
+ 10
+
+
+ 28
+ 6
+
+
+ 35
+ 18
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/fortress_heightmap.png
+ 257 257 50
+ 0 0 -28
+
+
+
+
+
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Fortress heightmap/tip/files/materials/textures/fortress_heightmap.png
+ 257 257 50
+ 0 0 -28
+
+
+
+
+
+
diff --git a/examples/worlds/lights.sdf b/examples/worlds/lights.sdf
index f1910f0ac7..61d2d92fd1 100644
--- a/examples/worlds/lights.sdf
+++ b/examples/worlds/lights.sdf
@@ -52,6 +52,7 @@
0.01
false
+ false
diff --git a/examples/worlds/model_photo_shoot.sdf b/examples/worlds/model_photo_shoot.sdf
new file mode 100644
index 0000000000..84ec5b913d
--- /dev/null
+++ b/examples/worlds/model_photo_shoot.sdf
@@ -0,0 +1,55 @@
+
+
+
+
+ 0 0 0
+
+
+
+ ogre2
+ 1, 1, 1
+
+
+ https://fuel.ignitionrobotics.org/1.0/OpenRobotics/models/Robonaut
+
+ poses.txt
+ false
+
+
+
+
+ 0 0 0 0 0 0
+
+
+ 1.047
+
+ 960
+ 540
+
+
+ 0.1
+ 100
+
+
+ 1
+ 30
+ true
+ camera
+
+
+ true
+
+
+
diff --git a/include/ignition/gazebo/ServerConfig.hh b/include/ignition/gazebo/ServerConfig.hh
index a975cc7bc4..7311d092e8 100644
--- a/include/ignition/gazebo/ServerConfig.hh
+++ b/include/ignition/gazebo/ServerConfig.hh
@@ -24,6 +24,7 @@
#include
#include
#include
+#include
#include
#include
@@ -42,6 +43,23 @@ namespace ignition
/// configuration.
class IGNITION_GAZEBO_VISIBLE ServerConfig
{
+ /// \brief Type of SDF source.
+ public: enum class SourceType
+ {
+ // No source specified.
+ kNone,
+
+ // The source is an SDF Root object.
+ kSdfRoot,
+
+ // The source is an SDF file.
+ kSdfFile,
+
+ // The source is an SDF string.
+ kSdfString,
+ };
+
+
class PluginInfoPrivate;
/// \brief Information about a plugin that should be loaded by the
/// server.
@@ -175,6 +193,17 @@ namespace ignition
/// \return The full contents of the SDF string, or empty string.
public: std::string SdfString() const;
+ /// \brief Set the SDF Root DOM object. The sdf::Root object will take
+ /// precendence over ServerConfig::SdfString() and
+ /// ServerConfig::SdfFile().
+ /// \param[in] _root SDF Root object to use.
+ public: void SetSdfRoot(const sdf::Root &_root) const;
+
+ /// \brief Get the SDF Root DOM object.
+ /// \return SDF Root object to use, or std::nullopt if the sdf::Root
+ /// has not been set via ServerConfig::SetSdfRoot().
+ public: std::optional &SdfRoot() const;
+
/// \brief Set the update rate in Hertz. Value <=0 are ignored.
/// \param[in] _hz The desired update rate of the server in Hertz.
public: void SetUpdateRate(const double &_hz);
@@ -383,6 +412,10 @@ namespace ignition
public: const std::chrono::time_point &
Timestamp() const;
+ /// \brief Get the type of source
+ /// \return The source type.
+ public: SourceType Source() const;
+
/// \brief Private data pointer
private: std::unique_ptr dataPtr;
};
diff --git a/include/ignition/gazebo/Util.hh b/include/ignition/gazebo/Util.hh
index 67e405659f..18cc6798c9 100644
--- a/include/ignition/gazebo/Util.hh
+++ b/include/ignition/gazebo/Util.hh
@@ -210,6 +210,25 @@ namespace ignition
const EntityComponentManager &_ecm,
bool _excludeWorld = true);
+ /// \brief Convert an SDF world filename string, such as "shapes.sdf", to
+ /// full system file path.
+ /// The provided SDF filename may be a Fuel URI, relative path, name
+ /// of an installed Gazebo world filename, or an absolute path.
+ /// \param[in] _sdfFile An SDF world filename such as:
+ /// 1. "shapes.sdf" - This is referencing an installed world file.
+ /// 2. "../shapes.sdf" - This is referencing a relative world file.
+ /// 3. "/home/user/shapes.sdf" - This is reference an absolute world
+ /// file.
+ /// 4. "https://fuel.ignitionrobotics.org/1.0/openrobotics/worlds/shapes.sdf"
+ /// This is referencing a Fuel URI. This will download the world file.
+ /// \param[in] _fuelResourceCache Path to a Fuel resource cache, if
+ /// known.
+ /// \return Full path to the SDF world file. An empty string is returned
+ /// if the file could not be found.
+ std::string IGNITION_GAZEBO_VISIBLE resolveSdfWorldFile(
+ const std::string &_sdfFilename,
+ const std::string &_fuelResourceCache = "");
+
/// \brief Helper function to "enable" a component (i.e. create it if it
/// doesn't exist) or "disable" a component (i.e. remove it if it exists).
/// \param[in] _ecm Mutable reference to the ECM
diff --git a/include/ignition/gazebo/components/WheelSlipCmd.hh b/include/ignition/gazebo/components/WheelSlipCmd.hh
new file mode 100644
index 0000000000..4daf7ee249
--- /dev/null
+++ b/include/ignition/gazebo/components/WheelSlipCmd.hh
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifndef IGNITION_GAZEBO_COMPONENTS_WHEELSLIPCMD_HH_
+#define IGNITION_GAZEBO_COMPONENTS_WHEELSLIPCMD_HH_
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+namespace ignition
+{
+namespace gazebo
+{
+// Inline bracket to help doxygen filtering.
+inline namespace IGNITION_GAZEBO_VERSION_NAMESPACE {
+namespace components
+{
+ /// \brief A component type that contains commanded wheel slip parameters of
+ /// an entity in the world frame represented by msgs::WheelSlipParameters.
+ using WheelSlipCmd = Component;
+ IGN_GAZEBO_REGISTER_COMPONENT("ign_gazebo_components.WheelSlipCmd",
+ WheelSlipCmd)
+}
+}
+}
+}
+#endif
diff --git a/src/Conversions.cc b/src/Conversions.cc
index bdc67fd9cb..549c1e527f 100644
--- a/src/Conversions.cc
+++ b/src/Conversions.cc
@@ -570,6 +570,25 @@ msgs::Light ignition::gazebo::convert(const sdf::Light &_in)
out.set_spot_inner_angle(_in.SpotInnerAngle().Radian());
out.set_spot_outer_angle(_in.SpotOuterAngle().Radian());
out.set_spot_falloff(_in.SpotFalloff());
+
+ {
+ // todo(ahcorde) Use the field is_light_off in light.proto from
+ // Garden on.
+ auto header = out.mutable_header()->add_data();
+ header->set_key("isLightOn");
+ std::string *value = header->add_value();
+ *value = std::to_string(_in.LightOn());
+ }
+
+ {
+ // todo(ahcorde) Use the field visualize_visual in light.proto from
+ // Garden on.
+ auto header = out.mutable_header()->add_data();
+ header->set_key("visualizeVisual");
+ std::string *value = header->add_value();
+ *value = std::to_string(_in.Visualize());
+ }
+
if (_in.Type() == sdf::LightType::POINT)
out.set_type(msgs::Light_LightType_POINT);
else if (_in.Type() == sdf::LightType::SPOT)
@@ -599,6 +618,43 @@ sdf::Light ignition::gazebo::convert(const msgs::Light &_in)
out.SetSpotInnerAngle(math::Angle(_in.spot_inner_angle()));
out.SetSpotOuterAngle(math::Angle(_in.spot_outer_angle()));
out.SetSpotFalloff(_in.spot_falloff());
+
+ // todo(ahcorde) Use the field is_light_off in light.proto from
+ // Garden on.
+ bool visualizeVisual = true;
+ for (int i = 0; i < _in.header().data_size(); ++i)
+ {
+ for (int j = 0;
+ j < _in.header().data(i).value_size(); ++j)
+ {
+ if (_in.header().data(i).key() ==
+ "visualizeVisual")
+ {
+ visualizeVisual = ignition::math::parseInt(
+ _in.header().data(i).value(0));
+ }
+ }
+ }
+ out.SetVisualize(visualizeVisual);
+
+ // todo(ahcorde) Use the field is_light_off in light.proto from
+ // Garden on.
+ bool isLightOn = true;
+ for (int i = 0; i < _in.header().data_size(); ++i)
+ {
+ for (int j = 0;
+ j < _in.header().data(i).value_size(); ++j)
+ {
+ if (_in.header().data(i).key() ==
+ "isLightOn")
+ {
+ isLightOn = ignition::math::parseInt(
+ _in.header().data(i).value(0));
+ }
+ }
+ }
+ out.SetLightOn(isLightOn);
+
if (_in.type() == msgs::Light_LightType_POINT)
out.SetType(sdf::LightType::POINT);
else if (_in.type() == msgs::Light_LightType_SPOT)
diff --git a/src/EntityComponentManager.cc b/src/EntityComponentManager.cc
index d512f8c0e8..201f21bf01 100644
--- a/src/EntityComponentManager.cc
+++ b/src/EntityComponentManager.cc
@@ -1855,6 +1855,8 @@ void EntityComponentManager::SetState(
components::BaseComponent *comp =
this->ComponentImplementation(entity, compIter.first);
+ std::istringstream istr(compMsg.component());
+
// Create if new
if (nullptr == comp)
{
@@ -1868,7 +1870,6 @@ void EntityComponentManager::SetState(
continue;
}
- std::istringstream istr(compMsg.component());
newComp->Deserialize(istr);
this->CreateComponentImplementation(entity,
@@ -1877,7 +1878,6 @@ void EntityComponentManager::SetState(
// Update component value
else
{
- std::istringstream istr(compMsg.component());
comp->Deserialize(istr);
this->SetChanged(entity, compIter.first,
_stateMsg.has_one_time_component_changes() ?
@@ -1958,6 +1958,10 @@ void EntityComponentManager::SetChanged(
auto oneTimeIter = this->dataPtr->oneTimeChangedComponents.find(_type);
if (oneTimeIter != this->dataPtr->oneTimeChangedComponents.end())
oneTimeIter->second.erase(_entity);
+
+ // the component state is flagged as no change, so don't mark the
+ // corresponding entity as one with a modified component
+ return;
}
this->dataPtr->AddModifiedComponent(_entity);
diff --git a/src/EntityComponentManager_TEST.cc b/src/EntityComponentManager_TEST.cc
index 4305803f48..63eac65d1d 100644
--- a/src/EntityComponentManager_TEST.cc
+++ b/src/EntityComponentManager_TEST.cc
@@ -2251,6 +2251,17 @@ TEST_P(EntityComponentManagerFixture,
EXPECT_EQ(ComponentState::NoChange,
manager.ComponentState(e2, c2->TypeId()));
+ // Marking a component that isn't changed as unchanged again shouldn't effect
+ // the ecm's changed state
+ manager.RunClearNewlyCreatedEntities();
+ EXPECT_EQ(0, manager.ChangedState().entities_size());
+ manager.SetChanged(e1, c1->TypeId(), ComponentState::NoChange);
+ EXPECT_EQ(ComponentState::NoChange,
+ manager.ComponentState(e1, c1->TypeId()));
+ EXPECT_FALSE(manager.HasOneTimeComponentChanges());
+ EXPECT_EQ(0u, manager.ComponentTypesWithPeriodicChanges().size());
+ EXPECT_EQ(0, manager.ChangedState().entities_size());
+
// Mark as changed
manager.SetChanged(e1, c1->TypeId(), ComponentState::PeriodicChange);
diff --git a/src/ModelCommandAPI_TEST.cc b/src/ModelCommandAPI_TEST.cc
index 36eafc23f4..5102d589ec 100644
--- a/src/ModelCommandAPI_TEST.cc
+++ b/src/ModelCommandAPI_TEST.cc
@@ -20,7 +20,7 @@
#include
#include
-#include
+#include
#include "ignition/gazebo/Server.hh"
#include "ignition/gazebo/test_config.hh" // NOLINT(build/include)
@@ -595,7 +595,7 @@ TEST(ModelCommandAPI, MagnetometerSensor)
/////////////////////////////////////////////////
// Tests `ign model -s` command with an rgbd camera.
-TEST(ModelCommandAPI, RgbdCameraSensor)
+TEST(ModelCommandAPI, IGN_UTILS_TEST_DISABLED_ON_MAC(RgbdCameraSensor))
{
ignition::gazebo::ServerConfig serverConfig;
// Using an static model to avoid any movements in the simulation.
diff --git a/src/Server.cc b/src/Server.cc
index 9b2a2b8448..87381ee516 100644
--- a/src/Server.cc
+++ b/src/Server.cc
@@ -33,31 +33,6 @@
using namespace ignition;
using namespace gazebo;
-//////////////////////////////////////////////////
-// Getting the first .sdf file in the path
-std::string findFuelResourceSdf(const std::string &_path)
-{
- if (!common::exists(_path))
- return "";
-
- for (common::DirIter file(_path); file != common::DirIter(); ++file)
- {
- std::string current(*file);
- if (!common::isFile(current))
- continue;
-
- auto fileName = common::basename(current);
- auto fileExtensionIndex = fileName.rfind(".");
- auto fileExtension = fileName.substr(fileExtensionIndex + 1);
-
- if (fileExtension == "sdf")
- {
- return current;
- }
- }
- return "";
-}
-
/// \brief This struct provides access to the default world.
struct DefaultWorld
{
@@ -98,83 +73,64 @@ Server::Server(const ServerConfig &_config)
sdf::Errors errors;
- // Load a world if specified. Check SDF string first, then SDF file
- if (!_config.SdfString().empty())
+ switch (_config.Source())
{
- std::string msg = "Loading SDF string. ";
- if (_config.SdfFile().empty())
+ // Load a world if specified. Check SDF string first, then SDF file
+ case ServerConfig::SourceType::kSdfRoot:
{
- msg += "File path not available.\n";
+ this->dataPtr->sdfRoot = _config.SdfRoot()->Clone();
+ ignmsg << "Loading SDF world from SDF DOM.\n";
+ break;
}
- else
- {
- msg += "File path [" + _config.SdfFile() + "].\n";
- }
- ignmsg << msg;
- errors = this->dataPtr->sdfRoot.LoadSdfString(_config.SdfString());
- }
- else if (!_config.SdfFile().empty())
- {
- std::string filePath;
- // Check Fuel if it's a URL
- auto sdfUri = common::URI(_config.SdfFile());
- if (sdfUri.Scheme() == "http" || sdfUri.Scheme() == "https")
+ case ServerConfig::SourceType::kSdfString:
{
- std::string fuelCachePath;
- if (this->dataPtr->fuelClient->CachedWorld(common::URI(_config.SdfFile()),
- fuelCachePath))
+ std::string msg = "Loading SDF string. ";
+ if (_config.SdfFile().empty())
{
- filePath = findFuelResourceSdf(fuelCachePath);
- }
- else if (auto result = this->dataPtr->fuelClient->DownloadWorld(
- common::URI(_config.SdfFile()), fuelCachePath))
- {
- filePath = findFuelResourceSdf(fuelCachePath);
+ msg += "File path not available.\n";
}
else
{
- ignwarn << "Fuel couldn't download URL [" << _config.SdfFile()
- << "], error: [" << result.ReadableResult() << "]"
- << std::endl;
+ msg += "File path [" + _config.SdfFile() + "].\n";
}
+ ignmsg << msg;
+ errors = this->dataPtr->sdfRoot.LoadSdfString(_config.SdfString());
+ break;
}
- if (filePath.empty())
+ case ServerConfig::SourceType::kSdfFile:
{
- common::SystemPaths systemPaths;
+ std::string filePath = resolveSdfWorldFile(_config.SdfFile(),
+ _config.ResourceCache());
- // Worlds from environment variable
- systemPaths.SetFilePathEnv(kResourcePathEnv);
+ if (filePath.empty())
+ {
+ ignerr << "Failed to find world [" << _config.SdfFile() << "]"
+ << std::endl;
+ return;
+ }
- // Worlds installed with ign-gazebo
- systemPaths.AddFilePaths(IGN_GAZEBO_WORLD_INSTALL_DIR);
+ ignmsg << "Loading SDF world file[" << filePath << "].\n";
- filePath = systemPaths.FindFile(_config.SdfFile());
+ // \todo(nkoenig) Async resource download.
+ // This call can block for a long period of time while
+ // resources are downloaded. Blocking here causes the GUI to block with
+ // a black screen (search for "Async resource download" in
+ // 'src/gui_main.cc'.
+ errors = this->dataPtr->sdfRoot.Load(filePath);
+ break;
}
- if (filePath.empty())
+ case ServerConfig::SourceType::kNone:
+ default:
{
- ignerr << "Failed to find world [" << _config.SdfFile() << "]"
- << std::endl;
- return;
+ ignmsg << "Loading default world.\n";
+ // Load an empty world.
+ /// \todo(nkoenig) Add a "AddWorld" function to sdf::Root.
+ errors = this->dataPtr->sdfRoot.LoadSdfString(DefaultWorld::World());
+ break;
}
-
- ignmsg << "Loading SDF world file[" << filePath << "].\n";
-
- // \todo(nkoenig) Async resource download.
- // This call can block for a long period of time while
- // resources are downloaded. Blocking here causes the GUI to block with
- // a black screen (search for "Async resource download" in
- // 'src/gui_main.cc'.
- errors = this->dataPtr->sdfRoot.Load(filePath);
- }
- else
- {
- ignmsg << "Loading default world.\n";
- // Load an empty world.
- /// \todo(nkoenig) Add a "AddWorld" function to sdf::Root.
- errors = this->dataPtr->sdfRoot.LoadSdfString(DefaultWorld::World());
}
if (!errors.empty())
diff --git a/src/ServerConfig.cc b/src/ServerConfig.cc
index fb0d9fdace..1024eb5d11 100644
--- a/src/ServerConfig.cc
+++ b/src/ServerConfig.cc
@@ -301,6 +301,12 @@ class ignition::gazebo::ServerConfigPrivate
/// \brief is the headless mode active.
public: bool isHeadlessRendering{false};
+
+ /// \brief Optional SDF root object.
+ public: std::optional sdfRoot;
+
+ /// \brief Type of source used.
+ public: ServerConfig::SourceType source{ServerConfig::SourceType::kNone};
};
//////////////////////////////////////////////////
@@ -321,8 +327,10 @@ ServerConfig::~ServerConfig() = default;
//////////////////////////////////////////////////
bool ServerConfig::SetSdfFile(const std::string &_file)
{
+ this->dataPtr->source = ServerConfig::SourceType::kSdfFile;
this->dataPtr->sdfFile = _file;
this->dataPtr->sdfString = "";
+ this->dataPtr->sdfRoot = std::nullopt;
return true;
}
@@ -335,8 +343,10 @@ std::string ServerConfig::SdfFile() const
//////////////////////////////////////////////////
bool ServerConfig::SetSdfString(const std::string &_sdfString)
{
+ this->dataPtr->source = ServerConfig::SourceType::kSdfString;
this->dataPtr->sdfFile = "";
this->dataPtr->sdfString = _sdfString;
+ this->dataPtr->sdfRoot = std::nullopt;
return true;
}
@@ -697,6 +707,35 @@ const std::vector &ServerConfig::LogRecordTopics() const
return this->dataPtr->logRecordTopics;
}
+/////////////////////////////////////////////////
+void ServerConfig::SetSdfRoot(const sdf::Root &_root) const
+{
+ this->dataPtr->source = ServerConfig::SourceType::kSdfRoot;
+ this->dataPtr->sdfRoot.emplace();
+
+ for (uint64_t i = 0; i < _root.WorldCount(); ++i)
+ {
+ const sdf::World *world = _root.WorldByIndex(i);
+ if (world)
+ this->dataPtr->sdfRoot->AddWorld(*world);
+ }
+
+ this->dataPtr->sdfFile = "";
+ this->dataPtr->sdfString = "";
+}
+
+/////////////////////////////////////////////////
+std::optional &ServerConfig::SdfRoot() const
+{
+ return this->dataPtr->sdfRoot;
+}
+
+/////////////////////////////////////////////////
+ServerConfig::SourceType ServerConfig::Source() const
+{
+ return this->dataPtr->source;
+}
+
/////////////////////////////////////////////////
void copyElement(sdf::ElementPtr _sdf, const tinyxml2::XMLElement *_xml)
{
diff --git a/src/ServerConfig_TEST.cc b/src/ServerConfig_TEST.cc
index 733a55ef37..bc93120ef2 100644
--- a/src/ServerConfig_TEST.cc
+++ b/src/ServerConfig_TEST.cc
@@ -228,3 +228,31 @@ TEST(ServerConfig, GenerateRecordPlugin)
EXPECT_EQ(plugin.Name(), "ignition::gazebo::systems::LogRecord");
}
+//////////////////////////////////////////////////
+TEST(ServerConfig, SdfRoot)
+{
+ ServerConfig config;
+ EXPECT_FALSE(config.SdfRoot());
+ EXPECT_TRUE(config.SdfFile().empty());
+ EXPECT_TRUE(config.SdfString().empty());
+ EXPECT_EQ(ServerConfig::SourceType::kNone, config.Source());
+
+ config.SetSdfString("string");
+ EXPECT_FALSE(config.SdfRoot());
+ EXPECT_TRUE(config.SdfFile().empty());
+ EXPECT_FALSE(config.SdfString().empty());
+ EXPECT_EQ(ServerConfig::SourceType::kSdfString, config.Source());
+
+ config.SetSdfFile("file");
+ EXPECT_FALSE(config.SdfRoot());
+ EXPECT_FALSE(config.SdfFile().empty());
+ EXPECT_TRUE(config.SdfString().empty());
+ EXPECT_EQ(ServerConfig::SourceType::kSdfFile, config.Source());
+
+ sdf::Root root;
+ config.SetSdfRoot(root);
+ EXPECT_TRUE(config.SdfRoot());
+ EXPECT_TRUE(config.SdfFile().empty());
+ EXPECT_TRUE(config.SdfString().empty());
+ EXPECT_EQ(ServerConfig::SourceType::kSdfRoot, config.Source());
+}
diff --git a/src/Server_TEST.cc b/src/Server_TEST.cc
index 879806b2e3..a2c6d1803d 100644
--- a/src/Server_TEST.cc
+++ b/src/Server_TEST.cc
@@ -295,6 +295,49 @@ TEST_P(ServerFixture, IGN_UTILS_TEST_DISABLED_ON_WIN32(SdfServerConfig))
EXPECT_FALSE(server.HasEntity("bad", 1));
}
+/////////////////////////////////////////////////
+TEST_P(ServerFixture, IGN_UTILS_TEST_DISABLED_ON_WIN32(SdfRootServerConfig))
+{
+ ignition::gazebo::ServerConfig serverConfig;
+
+ serverConfig.SetSdfString(TestWorldSansPhysics::World());
+ EXPECT_TRUE(serverConfig.SdfFile().empty());
+ EXPECT_FALSE(serverConfig.SdfString().empty());
+
+ serverConfig.SetSdfFile(common::joinPaths(PROJECT_SOURCE_PATH,
+ "test", "worlds", "air_pressure.sdf"));
+ EXPECT_FALSE(serverConfig.SdfFile().empty());
+ EXPECT_TRUE(serverConfig.SdfString().empty());
+
+ sdf::Root root;
+ root.Load(common::joinPaths(PROJECT_SOURCE_PATH,
+ "test", "worlds", "shapes.sdf"));
+
+ // Setting the SDF Root should override the string and file.
+ serverConfig.SetSdfRoot(root);
+
+ EXPECT_TRUE(serverConfig.SdfRoot());
+ EXPECT_TRUE(serverConfig.SdfFile().empty());
+ EXPECT_TRUE(serverConfig.SdfString().empty());
+
+ gazebo::Server server(serverConfig);
+ EXPECT_FALSE(server.Running());
+ EXPECT_FALSE(*server.Running(0));
+ EXPECT_TRUE(*server.Paused());
+ EXPECT_EQ(0u, *server.IterationCount());
+ EXPECT_EQ(24u, *server.EntityCount());
+ EXPECT_EQ(3u, *server.SystemCount());
+
+ EXPECT_TRUE(server.HasEntity("box"));
+ EXPECT_FALSE(server.HasEntity("box", 1));
+ EXPECT_TRUE(server.HasEntity("sphere"));
+ EXPECT_TRUE(server.HasEntity("cylinder"));
+ EXPECT_TRUE(server.HasEntity("capsule"));
+ EXPECT_TRUE(server.HasEntity("ellipsoid"));
+ EXPECT_FALSE(server.HasEntity("bad", 0));
+ EXPECT_FALSE(server.HasEntity("bad", 1));
+}
+
/////////////////////////////////////////////////
TEST_P(ServerFixture, IGN_UTILS_TEST_DISABLED_ON_WIN32(ServerConfigLogRecord))
{
diff --git a/src/SimulationRunner.cc b/src/SimulationRunner.cc
index 6edd7c63d3..0481a2df21 100644
--- a/src/SimulationRunner.cc
+++ b/src/SimulationRunner.cc
@@ -175,9 +175,10 @@ SimulationRunner::SimulationRunner(const sdf::World *_world,
// Load any additional plugins from the Server Configuration
this->LoadServerPlugins(this->serverConfig.Plugins());
- // If we have reached this point and no systems have been loaded, then load
- // a default set of systems.
- if (this->systemMgr->TotalCount() == 0)
+ // If we have reached this point and no world systems have been loaded, then
+ // load a default set of systems.
+ if (this->systemMgr->TotalByEntity(
+ worldEntity(this->entityCompMgr)).empty())
{
ignmsg << "No systems loaded from SDF, loading defaults" << std::endl;
bool isPlayback = !this->serverConfig.LogPlaybackPath().empty();
diff --git a/src/SimulationRunner_TEST.cc b/src/SimulationRunner_TEST.cc
index 17641a873c..3d525f0f23 100644
--- a/src/SimulationRunner_TEST.cc
+++ b/src/SimulationRunner_TEST.cc
@@ -1532,6 +1532,30 @@ TEST_P(SimulationRunnerTest,
componentId)) << componentId;
}
+/////////////////////////////////////////////////
+TEST_P(SimulationRunnerTest,
+ IGN_UTILS_TEST_DISABLED_ON_WIN32(LoadOnlyModelPlugin) )
+{
+ sdf::Root rootWithout;
+ rootWithout.Load(common::joinPaths(PROJECT_SOURCE_PATH,
+ "test", "worlds", "model_plugin_only.sdf"));
+ ASSERT_EQ(1u, rootWithout.WorldCount());
+
+ // ServerConfig will fall back to environment variable
+ auto config = common::joinPaths(PROJECT_SOURCE_PATH,
+ "test", "worlds", "server_valid2.config");
+ ASSERT_EQ(true, common::setenv(gazebo::kServerConfigPathEnv, config));
+ ServerConfig serverConfig;
+
+ // Create simulation runner
+ auto systemLoader = std::make_shared();
+ SimulationRunner runner(rootWithout.WorldByIndex(0), systemLoader,
+ serverConfig);
+
+ // 1 model plugin from SDF and 2 world plugins from config
+ ASSERT_EQ(3u, runner.SystemCount());
+}
+
/////////////////////////////////////////////////
TEST_P(SimulationRunnerTest, GuiInfo)
{
diff --git a/src/SystemInternal.hh b/src/SystemInternal.hh
index f8703f098f..cca49a965b 100644
--- a/src/SystemInternal.hh
+++ b/src/SystemInternal.hh
@@ -39,27 +39,33 @@ namespace ignition
{
/// \brief Constructor
/// \param[in] _systemPlugin A system loaded from a plugin.
- public: explicit SystemInternal(SystemPluginPtr _systemPlugin)
+ /// \param[in] _entity The entity that this system is attached to.
+ public: explicit SystemInternal(SystemPluginPtr _systemPlugin,
+ Entity _entity)
: systemPlugin(std::move(_systemPlugin)),
system(systemPlugin->QueryInterface()),
configure(systemPlugin->QueryInterface()),
reset(systemPlugin->QueryInterface()),
preupdate(systemPlugin->QueryInterface()),
update(systemPlugin->QueryInterface()),
- postupdate(systemPlugin->QueryInterface())
+ postupdate(systemPlugin->QueryInterface()),
+ parentEntity(_entity)
{
}
/// \brief Constructor
/// \param[in] _system Pointer to a system.
- public: explicit SystemInternal(const std::shared_ptr &_system)
+ /// \param[in] _entity The entity that this system is attached to.
+ public: explicit SystemInternal(const std::shared_ptr &_system,
+ Entity _entity)
: systemShared(_system),
system(_system.get()),
configure(dynamic_cast(_system.get())),
reset(dynamic_cast(_system.get())),
preupdate(dynamic_cast(_system.get())),
update(dynamic_cast(_system.get())),
- postupdate(dynamic_cast(_system.get()))
+ postupdate(dynamic_cast(_system.get())),
+ parentEntity(_entity)
{
}
@@ -95,9 +101,9 @@ namespace ignition
/// Will be nullptr if the System doesn't implement this interface.
public: ISystemPostUpdate *postupdate = nullptr;
- /// \brief Cached entity that was used to call `Configure` on the system
- /// Useful for if a system needs to be reconfigured at runtime
- public: Entity configureEntity = {kNullEntity};
+ /// \brief Entity that the system is attached to. It's passed to the
+ /// system during the `Configure` call.
+ public: Entity parentEntity = {kNullEntity};
/// \brief Cached sdf that was used to call `Configure` on the system
/// Useful for if a system needs to be reconfigured at runtime
diff --git a/src/SystemManager.cc b/src/SystemManager.cc
index 53085641e4..bde885612c 100644
--- a/src/SystemManager.cc
+++ b/src/SystemManager.cc
@@ -106,7 +106,7 @@ void SystemManager::AddSystem(const SystemPluginPtr &_system,
Entity _entity,
std::shared_ptr _sdf)
{
- this->AddSystemImpl(SystemInternal(_system), _entity, _sdf);
+ this->AddSystemImpl(SystemInternal(_system, _entity), _sdf);
}
//////////////////////////////////////////////////
@@ -115,19 +115,18 @@ void SystemManager::AddSystem(
Entity _entity,
std::shared_ptr _sdf)
{
- this->AddSystemImpl(SystemInternal(_system), _entity, _sdf);
+ this->AddSystemImpl(SystemInternal(_system, _entity), _sdf);
}
//////////////////////////////////////////////////
void SystemManager::AddSystemImpl(
SystemInternal _system,
- Entity _entity,
std::shared_ptr _sdf)
{
// Configure the system, if necessary
if (_system.configure && this->entityCompMgr && this->eventMgr)
{
- _system.configure->Configure(_entity, _sdf,
+ _system.configure->Configure(_system.parentEntity, _sdf,
*this->entityCompMgr,
*this->eventMgr);
}
@@ -166,3 +165,20 @@ const std::vector& SystemManager::SystemsPostUpdate()
{
return this->systemsPostupdate;
}
+
+//////////////////////////////////////////////////
+std::vector SystemManager::TotalByEntity(Entity _entity)
+{
+ std::vector result;
+ for (auto system : this->systems)
+ {
+ if (system.parentEntity == _entity)
+ result.push_back(system);
+ }
+ for (auto system : this->pendingSystems)
+ {
+ if (system.parentEntity == _entity)
+ result.push_back(system);
+ }
+ return result;
+}
diff --git a/src/SystemManager.hh b/src/SystemManager.hh
index 89a8f89d63..f6f1e214a2 100644
--- a/src/SystemManager.hh
+++ b/src/SystemManager.hh
@@ -92,29 +92,36 @@ namespace ignition
/// \return The number of newly-active systems
public: size_t ActivatePendingSystems();
- /// \brief Get an vector of all systems implementing "Configure"
+ /// \brief Get an vector of all active systems implementing "Configure"
+ /// \return Vector of systems's configure interfaces.
public: const std::vector& SystemsConfigure();
- /// \brief Get an vector of all systems implementing "Reset"
+ /// \brief Get an vector of all active systems implementing "Reset"
+ /// \return Vector of systems's reset interfaces.
public: const std::vector& SystemsReset();
- /// \brief Get an vector of all systems implementing "PreUpdate"
+ /// \brief Get an vector of all active systems implementing "PreUpdate"
+ /// \return Vector of systems's pre-update interfaces.
public: const std::vector& SystemsPreUpdate();
- /// \brief Get an vector of all systems implementing "Update"
+ /// \brief Get an vector of all active systems implementing "Update"
+ /// \return Vector of systems's update interfaces.
public: const std::vector& SystemsUpdate();
- /// \brief Get an vector of all systems implementing "PostUpdate"
+ /// \brief Get an vector of all active systems implementing "PostUpdate"
+ /// \return Vector of systems's post-update interfaces.
public: const std::vector& SystemsPostUpdate();
+ /// \brief Get an vector of all systems attached to a given entity.
+ /// \return Vector of systems.
+ public: std::vector TotalByEntity(Entity _entity);
+
/// \brief Implementation for AddSystem functions. This only adds systems
/// to a queue, the actual addition is performed by `AddSystemToRunner` at
/// the appropriate time.
/// \param[in] _system Generic representation of a system.
- /// \param[in] _entity Entity received from AddSystem.
/// \param[in] _sdf SDF received from AddSystem.
private: void AddSystemImpl(SystemInternal _system,
- Entity _entity,
std::shared_ptr _sdf);
/// \brief All the systems.
diff --git a/src/SystemManager_TEST.cc b/src/SystemManager_TEST.cc
index 4fc7288580..454f53e45a 100644
--- a/src/SystemManager_TEST.cc
+++ b/src/SystemManager_TEST.cc
@@ -93,7 +93,8 @@ TEST(SystemManager, AddSystemNoEcm)
EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size());
auto configSystem = std::make_shared();
- systemMgr.AddSystem(configSystem, kNullEntity, nullptr);
+ Entity configEntity{123u};
+ systemMgr.AddSystem(configSystem, configEntity, nullptr);
// SystemManager without an ECM/EventmManager will mean no config occurs
EXPECT_EQ(0, configSystem->configured);
@@ -105,6 +106,7 @@ TEST(SystemManager, AddSystemNoEcm)
EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size());
+ EXPECT_EQ(1u, systemMgr.TotalByEntity(configEntity).size());
systemMgr.ActivatePendingSystems();
EXPECT_EQ(1u, systemMgr.ActiveCount());
@@ -114,9 +116,11 @@ TEST(SystemManager, AddSystemNoEcm)
EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size());
+ EXPECT_EQ(1u, systemMgr.TotalByEntity(configEntity).size());
auto updateSystem = std::make_shared();
- systemMgr.AddSystem(updateSystem, kNullEntity, nullptr);
+ Entity updateEntity{456u};
+ systemMgr.AddSystem(updateSystem, updateEntity, nullptr);
EXPECT_EQ(1u, systemMgr.ActiveCount());
EXPECT_EQ(1u, systemMgr.PendingCount());
EXPECT_EQ(2u, systemMgr.TotalCount());
@@ -124,6 +128,7 @@ TEST(SystemManager, AddSystemNoEcm)
EXPECT_EQ(0u, systemMgr.SystemsPreUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsUpdate().size());
EXPECT_EQ(0u, systemMgr.SystemsPostUpdate().size());
+ EXPECT_EQ(1u, systemMgr.TotalByEntity(updateEntity).size());
systemMgr.ActivatePendingSystems();
EXPECT_EQ(2u, systemMgr.ActiveCount());
@@ -133,6 +138,7 @@ TEST(SystemManager, AddSystemNoEcm)
EXPECT_EQ(1u, systemMgr.SystemsPreUpdate().size());
EXPECT_EQ(1u, systemMgr.SystemsUpdate().size());
EXPECT_EQ(1u, systemMgr.SystemsPostUpdate().size());
+ EXPECT_EQ(1u, systemMgr.TotalByEntity(updateEntity).size());
}
/////////////////////////////////////////////////
diff --git a/src/Util.cc b/src/Util.cc
index 0b11cf1c5b..b603697cf4 100644
--- a/src/Util.cc
+++ b/src/Util.cc
@@ -21,6 +21,9 @@
#include
#include
+#include
+#include
+
#include "ignition/gazebo/components/Actor.hh"
#include "ignition/gazebo/components/Collision.hh"
#include "ignition/gazebo/components/Joint.hh"
@@ -552,7 +555,80 @@ std::optional sphericalCoordinates(Entity _entity,
// Return degrees
return math::Vector3d(IGN_RTOD(rad.X()), IGN_RTOD(rad.Y()), rad.Z());
}
+
+//////////////////////////////////////////////////
+// Getting the first .sdf file in the path
+std::string findFuelResourceSdf(const std::string &_path)
+{
+ if (!common::exists(_path))
+ return "";
+
+ for (common::DirIter file(_path); file != common::DirIter(); ++file)
+ {
+ std::string current(*file);
+ if (!common::isFile(current))
+ continue;
+
+ auto fileName = common::basename(current);
+ auto fileExtensionIndex = fileName.rfind(".");
+ auto fileExtension = fileName.substr(fileExtensionIndex + 1);
+
+ if (fileExtension == "sdf")
+ {
+ return current;
+ }
+ }
+ return "";
+}
+
+//////////////////////////////////////////////////
+std::string resolveSdfWorldFile(const std::string &_sdfFile,
+ const std::string &_fuelResourceCache)
+{
+ std::string filePath;
+
+ // Check Fuel if it's a URL
+ auto sdfUri = common::URI(_sdfFile);
+ if (sdfUri.Scheme() == "http" || sdfUri.Scheme() == "https")
+ {
+ fuel_tools::ClientConfig config;
+ if (!_fuelResourceCache.empty())
+ config.SetCacheLocation(_fuelResourceCache);
+ fuel_tools::FuelClient fuelClient(config);
+
+ std::string fuelCachePath;
+ if (fuelClient.CachedWorld(common::URI(_sdfFile), fuelCachePath))
+ {
+ filePath = findFuelResourceSdf(fuelCachePath);
+ }
+ else if (auto result = fuelClient.DownloadWorld(
+ common::URI(_sdfFile), fuelCachePath))
+ {
+ filePath = findFuelResourceSdf(fuelCachePath);
+ }
+ else
+ {
+ ignwarn << "Fuel couldn't download URL [" << _sdfFile
+ << "], error: [" << result.ReadableResult() << "]"
+ << std::endl;
+ }
+ }
+
+ if (filePath.empty())
+ {
+ common::SystemPaths systemPaths;
+
+ // Worlds from environment variable
+ systemPaths.SetFilePathEnv(kResourcePathEnv);
+
+ // Worlds installed with ign-gazebo
+ systemPaths.AddFilePaths(IGN_GAZEBO_WORLD_INSTALL_DIR);
+
+ filePath = systemPaths.FindFile(_sdfFile);
+ }
+
+ return filePath;
+}
}
}
}
-
diff --git a/src/Util_TEST.cc b/src/Util_TEST.cc
index ac83bf2643..3f2b4443a9 100644
--- a/src/Util_TEST.cc
+++ b/src/Util_TEST.cc
@@ -21,6 +21,8 @@
#include
#include
+#include
+
#include "ignition/gazebo/components/Actor.hh"
#include "ignition/gazebo/components/Collision.hh"
#include "ignition/gazebo/components/Joint.hh"
@@ -716,3 +718,42 @@ TEST_F(UtilTest, EnableComponent)
EXPECT_FALSE(enableComponent(ecm, entity1, false));
EXPECT_EQ(nullptr, ecm.Component(entity1));
}
+
+/////////////////////////////////////////////////
+TEST_F(UtilTest, ResolveSdfWorldFile)
+{
+ // Test resolving a Fuel URI
+ fuel_tools::ClientConfig config;
+
+ // URI to a Fuel world.
+ std::string fuelUri =
+ "https://fuel.ignitionrobotics.org/1.0/openrobotics/worlds/test world";
+
+ // The expect path for the local Fuel world.
+ std::string expectedPath = common::joinPaths(
+ config.CacheLocation(), "fuel.ignitionrobotics.org",
+ "openrobotics", "worlds", "test world");
+
+ // Get the Fuel world.
+ std::string resolvedPath = resolveSdfWorldFile(fuelUri,
+ config.CacheLocation());
+
+ // The Fuel model has not been downloaded, so it should not exist.
+ EXPECT_FALSE(resolvedPath.empty());
+
+ // The expected path should be the first part of the resolved path. The
+ // resolved path will have extra world version information at the end.
+ EXPECT_EQ(0u, resolvedPath.find(expectedPath));
+
+ // Now try to resolve the downloaded world file using an aboslute path
+ EXPECT_EQ(resolvedPath, resolveSdfWorldFile(resolvedPath));
+
+ // The "shapes.sdf" world file should resolve.
+ EXPECT_FALSE(resolveSdfWorldFile("shapes.sdf").empty());
+
+ // A bad absolute path should return an empty string
+ EXPECT_TRUE(resolveSdfWorldFile("/invalid/does_not_exist.sdf").empty());
+
+ // A bad relative path should return an empty string
+ EXPECT_TRUE(resolveSdfWorldFile("../invalid/does_not_exist.sdf").empty());
+}
diff --git a/src/gui/plugins/component_inspector/CMakeLists.txt b/src/gui/plugins/component_inspector/CMakeLists.txt
index 367278b24d..d4bc795abc 100644
--- a/src/gui/plugins/component_inspector/CMakeLists.txt
+++ b/src/gui/plugins/component_inspector/CMakeLists.txt
@@ -1,4 +1,8 @@
gz_add_gui_plugin(ComponentInspector
- SOURCES ComponentInspector.cc
- QT_HEADERS ComponentInspector.hh
+ SOURCES
+ ComponentInspector.cc
+ Pose3d.cc
+ QT_HEADERS
+ ComponentInspector.hh
+ Pose3d.hh
)
diff --git a/src/gui/plugins/component_inspector/ComponentInspector.cc b/src/gui/plugins/component_inspector/ComponentInspector.cc
index 5dbfff76b4..5bf8124493 100644
--- a/src/gui/plugins/component_inspector/ComponentInspector.cc
+++ b/src/gui/plugins/component_inspector/ComponentInspector.cc
@@ -24,7 +24,6 @@
#include
#include
#include
-#include
#include "ignition/gazebo/components/Actor.hh"
#include "ignition/gazebo/components/AngularAcceleration.hh"
@@ -56,8 +55,6 @@
#include "ignition/gazebo/components/PerformerAffinity.hh"
#include "ignition/gazebo/components/Physics.hh"
#include "ignition/gazebo/components/PhysicsEnginePlugin.hh"
-#include "ignition/gazebo/components/Pose.hh"
-#include "ignition/gazebo/components/PoseCmd.hh"
#include "ignition/gazebo/components/RenderEngineGuiPlugin.hh"
#include "ignition/gazebo/components/RenderEngineServerPlugin.hh"
#include "ignition/gazebo/components/SelfCollide.hh"
@@ -75,6 +72,7 @@
#include "ignition/gazebo/gui/GuiEvents.hh"
#include "ComponentInspector.hh"
+#include "Pose3d.hh"
namespace ignition::gazebo
{
@@ -109,31 +107,19 @@ namespace ignition::gazebo
/// \brief Transport node for making command requests
public: transport::Node node;
+
+ /// \brief A map of component types to the function used to update it.
+ public: std::map
+ updateViewCbs;
+
+ /// \brief Handles all components displayed as a 3D pose.
+ public: std::unique_ptr pose3d;
};
}
using namespace ignition;
using namespace gazebo;
-//////////////////////////////////////////////////
-template<>
-void ignition::gazebo::setData(QStandardItem *_item, const math::Pose3d &_data)
-{
- if (nullptr == _item)
- return;
-
- _item->setData(QString("Pose3d"),
- ComponentsModel::RoleNames().key("dataType"));
- _item->setData(QList({
- QVariant(_data.Pos().X()),
- QVariant(_data.Pos().Y()),
- QVariant(_data.Pos().Z()),
- QVariant(_data.Rot().Roll()),
- QVariant(_data.Rot().Pitch()),
- QVariant(_data.Rot().Yaw())
- }), ComponentsModel::RoleNames().key("data"));
-}
-
//////////////////////////////////////////////////
template<>
void ignition::gazebo::setData(QStandardItem *_item, const msgs::Light &_data)
@@ -155,6 +141,36 @@ void ignition::gazebo::setData(QStandardItem *_item, const msgs::Light &_data)
lightType = 2;
}
+ bool visualizeVisual = true;
+ for (int i = 0; i < _data.header().data_size(); ++i)
+ {
+ for (int j = 0;
+ j < _data.header().data(i).value_size(); ++j)
+ {
+ if (_data.header().data(i).key() ==
+ "visualizeVisual")
+ {
+ visualizeVisual = ignition::math::parseInt(
+ _data.header().data(i).value(0));
+ }
+ }
+ }
+
+ bool isLightOn = true;
+ for (int i = 0; i < _data.header().data_size(); ++i)
+ {
+ for (int j = 0;
+ j < _data.header().data(i).value_size(); ++j)
+ {
+ if (_data.header().data(i).key() ==
+ "isLightOn")
+ {
+ isLightOn = ignition::math::parseInt(
+ _data.header().data(i).value(0));
+ }
+ }
+ }
+
_item->setData(QString("Light"),
ComponentsModel::RoleNames().key("dataType"));
_item->setData(QList({
@@ -178,7 +194,9 @@ void ignition::gazebo::setData(QStandardItem *_item, const msgs::Light &_data)
QVariant(_data.spot_outer_angle()),
QVariant(_data.spot_falloff()),
QVariant(_data.intensity()),
- QVariant(lightType)
+ QVariant(lightType),
+ QVariant(isLightOn),
+ QVariant(visualizeVisual)
}), ComponentsModel::RoleNames().key("data"));
}
@@ -454,6 +472,9 @@ void ComponentInspector::LoadConfig(const tinyxml2::XMLElement *)
// Connect model
this->Context()->setContextProperty(
"ComponentsModel", &this->dataPtr->componentsModel);
+
+ // Type-specific handlers
+ this->dataPtr->pose3d = std::make_unique(this);
}
//////////////////////////////////////////////////
@@ -737,12 +758,6 @@ void ComponentInspector::Update(const UpdateInfo &,
if (comp)
setData(item, comp->Data());
}
- else if (typeId == components::Pose::typeId)
- {
- auto comp = _ecm.Component(this->dataPtr->entity);
- if (comp)
- setData(item, comp->Data());
- }
else if (typeId == components::RenderEngineGuiPlugin::typeId)
{
auto comp = _ecm.Component(
@@ -864,18 +879,10 @@ void ComponentInspector::Update(const UpdateInfo &,
setUnit(item, "m/s");
}
}
- else if (typeId == components::WorldPose::typeId)
+ else if (this->dataPtr->updateViewCbs.find(typeId) !=
+ this->dataPtr->updateViewCbs.end())
{
- auto comp = _ecm.Component(this->dataPtr->entity);
- if (comp)
- setData(item, comp->Data());
- }
- else if (typeId == components::WorldPoseCmd::typeId)
- {
- auto comp = _ecm.Component(
- this->dataPtr->entity);
- if (comp)
- setData(item, comp->Data());
+ this->dataPtr->updateViewCbs[typeId](_ecm, item);
}
else if (typeId == components::Material::typeId)
{
@@ -909,6 +916,13 @@ void ComponentInspector::Update(const UpdateInfo &,
}
}
+/////////////////////////////////////////////////
+void ComponentInspector::AddUpdateViewCb(ComponentTypeId _id,
+ inspector::UpdateViewCb _cb)
+{
+ this->dataPtr->updateViewCbs[_id] = _cb;
+}
+
/////////////////////////////////////////////////
bool ComponentInspector::eventFilter(QObject *_obj, QEvent *_event)
{
@@ -998,26 +1012,6 @@ void ComponentInspector::SetPaused(bool _paused)
this->PausedChanged();
}
-/////////////////////////////////////////////////
-void ComponentInspector::OnPose(double _x, double _y, double _z, double _roll,
- double _pitch, double _yaw)
-{
- std::function cb =
- [](const ignition::msgs::Boolean &/*_rep*/, const bool _result)
- {
- if (!_result)
- ignerr << "Error setting pose" << std::endl;
- };
-
- ignition::msgs::Pose req;
- req.set_id(this->dataPtr->entity);
- msgs::Set(req.mutable_position(), math::Vector3d(_x, _y, _z));
- msgs::Set(req.mutable_orientation(), math::Quaterniond(_roll, _pitch, _yaw));
- auto poseCmdService = "/world/" + this->dataPtr->worldName
- + "/set_pose";
- this->dataPtr->node.Request(poseCmdService, req, cb);
-}
-
/////////////////////////////////////////////////
void ComponentInspector::OnLight(
double _rSpecular, double _gSpecular, double _bSpecular, double _aSpecular,
@@ -1025,7 +1019,8 @@ void ComponentInspector::OnLight(
double _attRange, double _attLinear, double _attConstant,
double _attQuadratic, bool _castShadows, double _directionX,
double _directionY, double _directionZ, double _innerAngle,
- double _outerAngle, double _falloff, double _intensity, int _type)
+ double _outerAngle, double _falloff, double _intensity, int _type,
+ bool _isLightOn, bool _visualizeVisual)
{
std::function cb =
[](const ignition::msgs::Boolean &/*_rep*/, const bool _result)
@@ -1035,6 +1030,23 @@ void ComponentInspector::OnLight(
};
ignition::msgs::Light req;
+ {
+ // todo(ahcorde) Use the field is_light_off in light.proto from
+ // Garden on.
+ auto header = req.mutable_header()->add_data();
+ header->set_key("isLightOn");
+ std::string *value = header->add_value();
+ *value = std::to_string(_isLightOn);
+ }
+ {
+ // todo(ahcorde) Use the field visualize_visual in light.proto from
+ // Garden on.
+ auto header = req.mutable_header()->add_data();
+ header->set_key("visualizeVisual");
+ std::string *value = header->add_value();
+ *value = std::to_string(_visualizeVisual);
+ }
+
req.set_name(this->dataPtr->entityName);
req.set_id(this->dataPtr->entity);
ignition::msgs::Set(req.mutable_diffuse(),
@@ -1237,6 +1249,18 @@ bool ComponentInspector::NestedModel() const
return this->dataPtr->nestedModel;
}
+/////////////////////////////////////////////////
+const std::string &ComponentInspector::WorldName() const
+{
+ return this->dataPtr->worldName;
+}
+
+/////////////////////////////////////////////////
+transport::Node &ComponentInspector::TransportNode()
+{
+ return this->dataPtr->node;
+}
+
// Register this plugin
IGNITION_ADD_PLUGIN(ignition::gazebo::ComponentInspector,
ignition::gui::Plugin)
diff --git a/src/gui/plugins/component_inspector/ComponentInspector.hh b/src/gui/plugins/component_inspector/ComponentInspector.hh
index 2b8bbd50c7..a4800795e2 100644
--- a/src/gui/plugins/component_inspector/ComponentInspector.hh
+++ b/src/gui/plugins/component_inspector/ComponentInspector.hh
@@ -25,13 +25,15 @@
#include
#include
-#include
#include
+#include
#include
#include
#include
+#include "Types.hh"
+
#include
Q_DECLARE_METATYPE(ignition::gazebo::ComponentTypeId)
@@ -69,12 +71,6 @@ namespace gazebo
template<>
void setData(QStandardItem *_item, const std::string &_data);
- /// \brief Specialized to set pose data.
- /// \param[in] _item Item whose data will be set.
- /// \param[in] _data Data to set.
- template<>
- void setData(QStandardItem *_item, const math::Pose3d &_data);
-
/// \brief Specialized to set light data.
/// \param[in] _item Item whose data will be set.
/// \param[in] _data Data to set.
@@ -228,15 +224,12 @@ namespace gazebo
// Documentation inherited
public: void Update(const UpdateInfo &, EntityComponentManager &) override;
- /// \brief Callback in Qt thread when pose changes.
- /// \param[in] _x X
- /// \param[in] _y Y
- /// \param[in] _z Z
- /// \param[in] _roll Roll
- /// \param[in] _pitch Pitch
- /// \param[in] _yaw Yaw
- public: Q_INVOKABLE void OnPose(double _x, double _y, double _z,
- double _roll, double _pitch, double _yaw);
+ /// \brief Add a callback that's called whenever there are updates from the
+ /// ECM to the view, for a given component type.
+ /// \param[in] _id The component type id
+ /// \param[in] _cb Function that's called when there are updates.
+ public: void AddUpdateViewCb(ComponentTypeId _id,
+ inspector::UpdateViewCb _cb);
/// \brief Callback in Qt thread when specular changes.
/// \param[in] _rSpecular specular red
@@ -260,6 +253,8 @@ namespace gazebo
/// \param[in] _falloff Falloff of the spotlight
/// \param[in] _intensity Intensity of the light
/// \param[in] _type light type
+ /// \param[in] _isLightOn is light on
+ /// \param[in] _visualizeVisual is visual enabled
public: Q_INVOKABLE void OnLight(
double _rSpecular, double _gSpecular, double _bSpecular,
double _aSpecular, double _rDiffuse, double _gDiffuse,
@@ -267,7 +262,8 @@ namespace gazebo
double _attLinear, double _attConstant, double _attQuadratic,
bool _castShadows, double _directionX, double _directionY,
double _directionZ, double _innerAngle, double _outerAngle,
- double _falloff, double _intensity, int _type);
+ double _falloff, double _intensity, int _type, bool _isLightOn,
+ bool _visualizeVisual);
/// \brief Callback in Qt thread when physics' properties change.
/// \param[in] _stepSize step size
@@ -368,6 +364,14 @@ namespace gazebo
/// \brief Notify that paused has changed.
signals: void PausedChanged();
+ /// \brief Name of world entity
+ /// \return World name
+ public: const std::string &WorldName() const;
+
+ /// \brief Node for communication
+ /// \return Transport node
+ public: transport::Node &TransportNode();
+
/// \internal
/// \brief Pointer to private data.
private: std::unique_ptr dataPtr;
diff --git a/src/gui/plugins/component_inspector/ComponentInspector.qml b/src/gui/plugins/component_inspector/ComponentInspector.qml
index 53a77abfd3..90a1e53dcb 100644
--- a/src/gui/plugins/component_inspector/ComponentInspector.qml
+++ b/src/gui/plugins/component_inspector/ComponentInspector.qml
@@ -101,12 +101,14 @@ Rectangle {
_rDiffuse, _gDiffuse, _bDiffuse, _aDiffuse,
_attRange, _attLinear, _attConstant, _attQuadratic,
_castShadows, _directionX, _directionY, _directionZ,
- _innerAngle, _outerAngle, _falloff, _intensity, _type) {
+ _innerAngle, _outerAngle, _falloff, _intensity, _type,
+ _isLightOn, _visualizeVisual) {
ComponentInspector.OnLight(_rSpecular, _gSpecular, _bSpecular, _aSpecular,
_rDiffuse, _gDiffuse, _bDiffuse, _aDiffuse,
_attRange, _attLinear, _attConstant, _attQuadratic,
_castShadows, _directionX, _directionY, _directionZ,
- _innerAngle, _outerAngle, _falloff, _intensity, _type)
+ _innerAngle, _outerAngle, _falloff, _intensity, _type,
+ _isLightOn, _visualizeVisual)
}
/*
diff --git a/src/gui/plugins/component_inspector/Light.qml b/src/gui/plugins/component_inspector/Light.qml
index 26bc72806b..081b88972d 100644
--- a/src/gui/plugins/component_inspector/Light.qml
+++ b/src/gui/plugins/component_inspector/Light.qml
@@ -99,6 +99,12 @@ Rectangle {
// Loaded item for intensity
property var intensityItem: {}
+ // Loaded item for isLightOn
+ property var isLightOnItem: {}
+
+ // Loaded item for visualizeVisuals
+ property var visualizeVisualItem: {}
+
// Send new light data to C++
function sendLight() {
// TODO(anyone) There's a loss of precision when these values get to C++
@@ -123,7 +129,9 @@ Rectangle {
outerAngleItem.value,
falloffItem.value,
intensityItem.value,
- model.data[20]
+ model.data[20],
+ isLightOnItem.checked,
+ visualizeVisualItem.checked
);
}
@@ -248,6 +256,68 @@ Rectangle {
id: grid
width: parent.width
+ RowLayout {
+ Rectangle {
+ color: "transparent"
+ height: 40
+ Layout.preferredWidth: visualizeVisualText.width + indentation*3
+
+ Text {
+ id : visualizeVisualText
+ text: ' View gizmo'
+ leftPadding: 5
+ color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
+ font.pointSize: 12
+ anchors.centerIn: parent
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ height: 40
+
+ Loader {
+ id: visualizeVisualLoader
+ anchors.fill: parent
+ property double numberValue: model.data[22]
+ sourceComponent: ignSwitch
+ onLoaded: {
+ visualizeVisualItem = visualizeVisualLoader.item
+ }
+ }
+ }
+ }
+
+ RowLayout {
+ Rectangle {
+ color: "transparent"
+ height: 40
+ Layout.preferredWidth: isOnText.width + indentation*3
+
+ Text {
+ id : isOnText
+ text: ' Turn on/off'
+ leftPadding: 5
+ color: Material.theme == Material.Light ? "#444444" : "#bbbbbb"
+ font.pointSize: 12
+ anchors.centerIn: parent
+ }
+ }
+ Item {
+ Layout.fillWidth: true
+ height: 40
+
+ Loader {
+ id: isOnLoader
+ anchors.fill: parent
+ property double numberValue: model.data[21]
+ sourceComponent: ignSwitch
+ onLoaded: {
+ isLightOnItem = isOnLoader.item
+ }
+ }
+ }
+ }
+
RowLayout {
Rectangle {
color: "transparent"
diff --git a/src/gui/plugins/component_inspector/Pose3d.cc b/src/gui/plugins/component_inspector/Pose3d.cc
new file mode 100644
index 0000000000..e3a9446202
--- /dev/null
+++ b/src/gui/plugins/component_inspector/Pose3d.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#include
+
+#include
+#include
+
+#include "Pose3d.hh"
+
+using namespace ignition;
+using namespace gazebo;
+using namespace inspector;
+
+/////////////////////////////////////////////////
+Pose3d::Pose3d(ComponentInspector *_inspector)
+{
+ _inspector->Context()->setContextProperty("Pose3dImpl", this);
+ this->inspector = _inspector;
+
+ this->inspector->AddUpdateViewCb(components::Pose::typeId,
+ std::bind(&Pose3d::UpdateView, this,
+ std::placeholders::_1, std::placeholders::_2));
+ this->inspector->AddUpdateViewCb(components::WorldPose::typeId,
+ std::bind(&Pose3d::UpdateView, this,
+ std::placeholders::_1, std::placeholders::_2));
+ this->inspector->AddUpdateViewCb(components::WorldPoseCmd::typeId,
+ std::bind(&Pose3d::UpdateView, this,
+ std::placeholders::_1, std::placeholders::_2));
+}
+
+/////////////////////////////////////////////////
+void Pose3d::OnPose(double _x, double _y, double _z, double _roll,
+ double _pitch, double _yaw)
+{
+ std::function cb =
+ [](const msgs::Boolean &, const bool _result)
+ {
+ if (!_result)
+ ignerr << "Error setting pose" << std::endl;
+ };
+
+ msgs::Pose req;
+ req.set_id(this->inspector->GetEntity());
+ msgs::Set(req.mutable_position(), math::Vector3d(_x, _y, _z));
+ msgs::Set(req.mutable_orientation(), math::Quaterniond(_roll, _pitch, _yaw));
+ std::string poseCmdService("/world/" + this->inspector->WorldName()
+ + "/set_pose");
+ this->inspector->TransportNode().Request(poseCmdService, req, cb);
+}
diff --git a/src/gui/plugins/component_inspector/Pose3d.hh b/src/gui/plugins/component_inspector/Pose3d.hh
new file mode 100644
index 0000000000..890cac2590
--- /dev/null
+++ b/src/gui/plugins/component_inspector/Pose3d.hh
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+#ifndef IGNITION_GAZEBO_GUI_COMPONENTINSPECTOR_POSE3D_HH_
+#define IGNITION_GAZEBO_GUI_COMPONENTINSPECTOR_POSE3D_HH_
+
+#include
+
+#include "ignition/gazebo/components/Pose.hh"
+#include "ignition/gazebo/components/PoseCmd.hh"
+#include "ignition/gazebo/EntityComponentManager.hh"
+
+#include "ComponentInspector.hh"
+#include "Types.hh"
+
+#include
+#include
+
+namespace ignition
+{
+namespace gazebo
+{
+class ComponentInspector;
+namespace inspector
+{
+ /// \brief Handles components that are displayed as a 3D pose:
+ /// * `components::Pose`
+ /// * `components::WorldPose`
+ /// * `components::WorldPoseCmd`
+ class Pose3d : public QObject
+ {
+ Q_OBJECT
+
+ /// \brief Constructor
+ /// \param[in] _inspector The component inspector.
+ public: explicit Pose3d(ComponentInspector *_inspector);
+
+ /// \brief Callback when there are ECM updates.
+ /// \param[in] _ecm Immutable reference to the ECM.
+ /// \param[in] _item Item to update.
+ /// \tparam ComponentType Type of component being updated.
+ public:
+ template
+ void UpdateView(const EntityComponentManager &_ecm,
+ QStandardItem *_item)
+ {
+ if (nullptr == _item)
+ return;
+
+ auto comp = _ecm.Component(this->inspector->GetEntity());
+ if (nullptr == comp)
+ return;
+
+ auto pose = comp->Data();
+
+ _item->setData(QString("Pose3d"),
+ ComponentsModel::RoleNames().key("dataType"));
+ _item->setData(QList({
+ QVariant(pose.Pos().X()),
+ QVariant(pose.Pos().Y()),
+ QVariant(pose.Pos().Z()),
+ QVariant(pose.Rot().Roll()),
+ QVariant(pose.Rot().Pitch()),
+ QVariant(pose.Rot().Yaw())
+ }), ComponentsModel::RoleNames().key("data"));
+ }
+
+ /// \brief Callback in Qt thread when pose changes.
+ /// \param[in] _x X
+ /// \param[in] _y Y
+ /// \param[in] _z Z
+ /// \param[in] _roll Roll
+ /// \param[in] _pitch Pitch
+ /// \param[in] _yaw Yaw
+ public: Q_INVOKABLE void OnPose(double _x, double _y, double _z,
+ double _roll, double _pitch, double _yaw);
+
+ /// \brief Pointer to the component inspector. This is used to add
+ /// callbacks.
+ private: ComponentInspector *inspector{nullptr};
+ };
+}
+}
+}
+#endif
diff --git a/src/gui/plugins/component_inspector/Pose3d.qml b/src/gui/plugins/component_inspector/Pose3d.qml
index e9806463e4..0690edd458 100644
--- a/src/gui/plugins/component_inspector/Pose3d.qml
+++ b/src/gui/plugins/component_inspector/Pose3d.qml
@@ -68,7 +68,7 @@ Rectangle {
// Send new pose to C++
function sendPose() {
// TODO(anyone) There's a loss of precision when these values get to C++
- componentInspector.onPose(
+ Pose3dImpl.OnPose(
xItem.value,
yItem.value,
zItem.value,
diff --git a/src/gui/plugins/component_inspector/Types.hh b/src/gui/plugins/component_inspector/Types.hh
new file mode 100644
index 0000000000..5e0b682027
--- /dev/null
+++ b/src/gui/plugins/component_inspector/Types.hh
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 Open Source Robotics Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+*/
+
+#ifndef IGNITION_GAZEBO_GUI_COMPONENTINSPECTOR_TYPES_HH_
+#define IGNITION_GAZEBO_GUI_COMPONENTINSPECTOR_TYPES_HH_
+
+#include
+
+#include
+
+namespace ignition
+{
+namespace gazebo
+{
+namespace inspector
+{
+ /// \brief Function definition that a component can use
+ /// to update its UI elements based on changes from the ECM.
+ /// * _ecm Immutable reference to the ECM
+ /// * _item Item to be updated
+ /// \sa ComponentInspector::AddUpdateViewCb
+ using UpdateViewCb = std::function;
+}
+}
+}
+#endif
+
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
index a1507b884c..ebba5b6c54 100644
--- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.cc
@@ -110,7 +110,7 @@ void EntityContextMenu::LoadConfig(const tinyxml2::XMLElement *)
this->dataPtr->entityContextMenuHandler);
if (this->title.empty())
- this->title = "EntityContextMenu";
+ this->title = "Entity Context Menu";
ignition::gui::App()->findChild
()->installEventFilter(this);
diff --git a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
index 66e2f56d28..98fd9afa71 100644
--- a/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
+++ b/src/gui/plugins/entity_context_menu/EntityContextMenuPlugin.qml
@@ -15,15 +15,39 @@
*
*/
-import QtQuick 2.0
-import QtQuick.Controls 2.0
+import QtQuick 2.9
+import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import RenderWindowOverlay 1.0
import IgnGazebo 1.0 as IgnGazebo
-Rectangle {
- visible: false
- color: "transparent"
+ColumnLayout {
+ Layout.minimumWidth: 350
+ Layout.minimumHeight: 370
+ anchors.fill: parent
+ anchors.margins: 10
+
+ property string message: 'Adding a right-click context menu to the 3D scene.
' +
+ 'For proper positioning:
' +
+ '- Undock the menu
' +
+ '- Right-click this text, and choose Settings
' +
+ '- Hide the title bar and set height to zero
' +
+ 'These other plugins must also be loaded to enable all functionality:
' +
+ '- Move To / Follow: Camera tracking
' +
+ '- Copy / Paste: Copy Paste
' +
+ '- View: Visualization Capabilities
' +
+ '- Remove: Gz Scene Manager
'
+
+ Label {
+ Layout.fillWidth: true
+ wrapMode: Text.WordWrap
+ text: message
+ }
+
+ Item {
+ width: 10
+ Layout.fillHeight: true
+ }
RenderWindowOverlay {
id: renderWindowOverlay
diff --git a/src/gui/plugins/scene_manager/GzSceneManager.cc b/src/gui/plugins/scene_manager/GzSceneManager.cc
index 48c0735f10..68d8a666c0 100644
--- a/src/gui/plugins/scene_manager/GzSceneManager.cc
+++ b/src/gui/plugins/scene_manager/GzSceneManager.cc
@@ -17,6 +17,9 @@
#include