diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 6de6d931b..c129ca4fe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -5,7 +5,7 @@ labels: bug --- +https://robotics.stackexchange.com instead.--> ## Environment * OS Version: diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 87233a479..52b56e336 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -6,7 +6,7 @@ labels: enhancement +https://robotics.stackexchange.com instead.--> ## Desired behavior diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab45c9400..bdac4dc4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,12 @@ name: Ubuntu CI -on: [push, pull_request] +on: + pull_request: + push: + branches: + - 'ign-gui[0-9]' + - 'gz-gui[0-9]?' + - 'main' jobs: jammy-ci: @@ -8,7 +14,7 @@ jobs: name: Ubuntu Jammy CI steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Compile and test id: ci uses: gazebo-tooling/action-gz-ci@jammy diff --git a/.github/workflows/package_xml.yml b/.github/workflows/package_xml.yml new file mode 100644 index 000000000..4bd4a9aa0 --- /dev/null +++ b/.github/workflows/package_xml.yml @@ -0,0 +1,11 @@ +name: Validate package.xml + +on: + pull_request: + +jobs: + package-xml: + runs-on: ubuntu-latest + name: Validate package.xml + steps: + - uses: gazebo-tooling/action-gz-ci/validate_package_xml@jammy diff --git a/Changelog.md b/Changelog.md index 4aff88aa6..71b64e06c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,6 +4,49 @@ ## Gazebo GUI 8 +### Gazebo GUI 8.3.0 (2024-06-20) + +1. Backport: Adding cone primitives. + * [Pull request #621](https://github.com/gazebosim/gz-gui/pull/621) + +### Gazebo GUI 8.2.0 (2024-06-14) + +1. Enhanced tracking camera and user visualization experience + * [Pull request #619](https://github.com/gazebosim/gz-gui/pull/619) + +1. Add package.xml + * [Pull request #613](https://github.com/gazebosim/gz-gui/pull/613) + +### Gazebo GUI 8.1.1 (2024-04-10) + +1. Fix compiler warnings in GCC-13 (Noble) + * [Pull request #616](https://github.com/gazebosim/gz-gui/pull/616) + +1. Use relative install paths for plugin shared libraries and gz-tools data + * [Pull request #614](https://github.com/gazebosim/gz-gui/pull/614) + +### Gazebo GUI 8.1.0 (2024-03-14) + +1. Update CI badges in README + * [Pull request #603](https://github.com/gazebosim/gz-gui/pull/603) + +1. Fix plugin filter on Windows + * [Pull request #567](https://github.com/gazebosim/gz-gui/pull/567) + +1. Tidy namespaces + * [Pull request #590](https://github.com/gazebosim/gz-gui/pull/590) + +1. Infrastructure + * [Pull request #591](https://github.com/gazebosim/gz-gui/pull/591) + * [Pull request #597](https://github.com/gazebosim/gz-gui/pull/597) + * [Pull request #608](https://github.com/gazebosim/gz-gui/pull/608) + +1. Add check for vulkan support in rendering + * [Pull request #589](https://github.com/gazebosim/gz-gui/pull/589) + +1. Add optional binary relocatability + * [Pull request #580](https://github.com/gazebosim/gz-gui/pull/580) + ### Gazebo GUI 8.0.0 (2023-09-29) 1. Use fully-qualified message names to avoid deprecation warning diff --git a/conf/CMakeLists.txt b/conf/CMakeLists.txt index 70201dfd8..4fb0a6a34 100644 --- a/conf/CMakeLists.txt +++ b/conf/CMakeLists.txt @@ -20,4 +20,4 @@ configure_file( # Install the yaml configuration files in an unversioned location. install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${GZ_DESIGNATION}${PROJECT_VERSION_MAJOR}.yaml - DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/gz/) + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gz/) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 0fd15b96c..7a1d464b3 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -25,5 +25,5 @@ if (DOXYGEN_FOUND) COMMENT "Generating API documentation with Doxygen" VERBATIM) install(FILES ${CMAKE_BINARY_DIR}/doc/${PROJECT_NAME_LOWER}.tag.xml - DESTINATION ${CMAKE_INSTALL_PREFIX}/share/gz/${PROJECT_NAME_LOWER}_${PROJECT_VERSION_MINOR}) + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/gz/${PROJECT_NAME_LOWER}_${PROJECT_VERSION_MINOR}) endif() diff --git a/examples/standalone/scene_provider/README.md b/examples/standalone/scene_provider/README.md index 3183a83e4..ce2da00fc 100644 --- a/examples/standalone/scene_provider/README.md +++ b/examples/standalone/scene_provider/README.md @@ -9,7 +9,7 @@ plugin to update the scene using Gazebo Transport. ## Build -``` +```bash cd examples/standalone/scene_provider mkdir build cd build @@ -21,14 +21,14 @@ make In one terminal, start the scene provider: -``` +```bash cd examples/standalone/scene_provider/build ./scene_provider ``` On another terminal, start the example config: -``` +```bash gz gui -c examples/config/scene3d.config ``` @@ -42,24 +42,48 @@ Some commands to test camera tracking with this demo: Move to box: -``` +```bash gz service -s /gui/move_to --reqtype gz.msgs.StringMsg --reptype gz.msgs.Boolean --timeout 2000 --req 'data: "box_model"' ``` Echo camera pose: -``` +```bash gz topic -e -t /gui/camera/pose ``` -Follow box: +Echo camera tracking information: +```bash +gz topic -e -t /gui/currently_tracked ``` -gz service -s /gui/follow --reqtype gz.msgs.StringMsg --reptype gz.msgs.Boolean --timeout 2000 --req 'data: "box_model"' + +Follow box from track topic: + +```bash +gz topic -t /gui/track -m gz.msgs.CameraTrack -p 'track_mode: 2, follow_target: { name: "box_model"}' ``` -Update follow offset: +Follow box from track topic: +```bash +gz topic -t /gui/track -m gz.msgs.CameraTrack -p 'track_mode: 2, follow_target: "box_model", follow_offset: {x: -1, y: 0, z: 1}' ``` + +Update follow offset from track topic: + +```bash +gz topic -t /gui/track -m gz.msgs.CameraTrack -p 'track_mode: 2, follow_target: {name: "box_model"}, follow_offset: {x: -1, y: 0, z: 1}' +``` + +Follow box from service (deprecated): + +```bash +gz service -s /gui/follow --reqtype gz.msgs.StringMsg --reptype gz.msgs.Boolean --timeout 2000 --req 'data: "box_model"' +``` + +Update follow offset from follow offset service (deprecated): + +```bash gz service -s /gui/follow/offset --reqtype gz.msgs.Vector3d --reptype gz.msgs.Boolean --timeout 2000 --req 'x: 5, y: 5, z: 5' ``` diff --git a/include/gz/gui/CMakeLists.txt b/include/gz/gui/CMakeLists.txt index 5e15317d6..c2994699e 100644 --- a/include/gz/gui/CMakeLists.txt +++ b/include/gz/gui/CMakeLists.txt @@ -58,4 +58,8 @@ target_link_libraries(${PROJECT_LIBRARY_TARGET_NAME} TINYXML2::TINYXML2 ) +target_compile_definitions(${PROJECT_LIBRARY_TARGET_NAME} PRIVATE + SHARED_LIBRARY_PREFIX=\"${CMAKE_SHARED_LIBRARY_PREFIX}\" + SHARED_LIBRARY_SUFFIX=\"${CMAKE_SHARED_LIBRARY_SUFFIX}\") + gz_install_all_headers() diff --git a/include/gz/gui/qml/Main.qml b/include/gz/gui/qml/Main.qml index c18154f29..c7e540200 100644 --- a/include/gz/gui/qml/Main.qml +++ b/include/gz/gui/qml/Main.qml @@ -189,8 +189,8 @@ ApplicationWindow contentItem: Image { fillMode: Image.Pad horizontalAlignment: Image.AlignHCenter - verticalAlignment: Image.AlignVCenter - source: "images/drawer.png" + verticalAlignment: Image.AlignVCenter + source: Material.theme === Material.Light ? "images/drawer.png" : "images/drawer_dark.png" } onClicked: drawer.open() } @@ -221,7 +221,7 @@ ApplicationWindow fillMode: Image.Pad horizontalAlignment: Image.AlignHCenter verticalAlignment: Image.AlignVCenter - source: "images/menu.png" + source: Material.theme === Material.Light ? "images/menu.png" : "images/menu_dark.png" } onClicked: pluginMenu.open() diff --git a/include/gz/gui/qml/images/drawer_dark.png b/include/gz/gui/qml/images/drawer_dark.png new file mode 100644 index 000000000..7e0089474 Binary files /dev/null and b/include/gz/gui/qml/images/drawer_dark.png differ diff --git a/include/gz/gui/qml/images/menu_dark.png b/include/gz/gui/qml/images/menu_dark.png new file mode 100644 index 000000000..6f4ad6eb9 Binary files /dev/null and b/include/gz/gui/qml/images/menu_dark.png differ diff --git a/include/gz/gui/resources.qrc b/include/gz/gui/resources.qrc index 8a18a99f6..1668662ab 100644 --- a/include/gz/gui/resources.qrc +++ b/include/gz/gui/resources.qrc @@ -24,7 +24,9 @@ qml/images/gazebo_logo.png qml/images/drawer.png + qml/images/drawer_dark.png qml/images/menu.png + qml/images/menu_dark.png qml/images/export_icon.png qml/images/search.svg qml/images/plottable_icon.svg diff --git a/package.xml b/package.xml new file mode 100644 index 000000000..592144e50 --- /dev/null +++ b/package.xml @@ -0,0 +1,53 @@ + + + + gz-gui9 + 9.0.0 + Gazebo GUI : Graphical interfaces for robotics applications + Jenn Nguyen + Apache License 2.0 + https://github.com/gazebosim/gz-rendering + + cmake + + gz-cmake3 + + gz-common6 + gz-math8 + gz-msgs11 + gz-plugin3 + gz-rendering9 + gz-tools2 + gz-transport14 + gz-utils3 + libqt5-core + libqt5-qml + libqt5-quick + libqt5-widgets + protobuf-dev + qml-module-qt-labs-folderlistmodel + qml-module-qt-labs-platform + qml-module-qt-labs-settings + qml-module-qtcharts + qml-module-qtgraphicaleffects + qml-module-qtlocation + qml-module-qtpositioning + qml-module-qtquick-controls2 + qml-module-qtquick-controls + qml-module-qtquick-dialogs + qml-module-qtquick-extras + qml-module-qtquick-layouts + qml-module-qtquick-templates2 + qml-module-qtquick-window2 + qml-module-qtquick2 + qtbase5-dev + qtdeclarative5-dev + qtquickcontrols2-5-dev + tinyxml2 + + xvfb + + + cmake + + diff --git a/src/Application.cc b/src/Application.cc index 5427a67c3..2a414003e 100644 --- a/src/Application.cc +++ b/src/Application.cc @@ -843,10 +843,19 @@ std::vector>> { auto plugin = common::basename(*dirIter); - // All we verify is that the file starts with "lib", any further - // checks would require loading the plugin. + // All we verify is that the file starts with shared library prefix and + // ends with shared library suffix, any further checks would require + // loading the plugin. - if (plugin.find("lib") == 0) + // TODO(anyone): Move this logic into gz-plugin to be reusable + + // This computation could underflow the unsigned range, but that is okay + // as in such case we would check if the suffix is placed somewhere much + // further than allowed filename length. + const auto suffixPos = plugin.length() - strlen(SHARED_LIBRARY_SUFFIX); + + if (plugin.find(SHARED_LIBRARY_PREFIX) == 0 && + plugin.rfind(SHARED_LIBRARY_SUFFIX) == suffixPos) ps.push_back(plugin); } diff --git a/src/MainWindow.cc b/src/MainWindow.cc index bff0186a9..0bc9eb101 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -128,8 +128,13 @@ QStringList MainWindow::PluginListModel() const { for (auto const &plugin : path.second) { - // Remove lib and .so - auto pluginName = plugin.substr(3, plugin.find(".") - 3); + // TODO(anyone): Move this into gz-plugin to be reusable + + // Remove shared library prefix and shared library suffix + auto pluginName = plugin.substr( + strlen(SHARED_LIBRARY_PREFIX), + plugin.length() - strlen(SHARED_LIBRARY_PREFIX) - + strlen(SHARED_LIBRARY_SUFFIX)); // Split WWWCamelCase3D -> WWW Camel Case 3D std::regex reg("(\\B[A-Z][a-z])|(\\B[0-9])"); diff --git a/src/cmd/CMakeLists.txt b/src/cmd/CMakeLists.txt index 821cd4acf..568de6289 100644 --- a/src/cmd/CMakeLists.txt +++ b/src/cmd/CMakeLists.txt @@ -51,4 +51,4 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/gui${PROJECT_VERSION_MAJOR}.bash_completion.sh DESTINATION - ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/gz/gz${GZ_TOOLS_VER}.completion.d) + ${CMAKE_INSTALL_DATAROOTDIR}/gz/gz${GZ_TOOLS_VER}.completion.d) diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt index f5bcf98f1..362df296a 100644 --- a/src/plugins/CMakeLists.txt +++ b/src/plugins/CMakeLists.txt @@ -110,12 +110,13 @@ function(gz_gui_add_plugin plugin_name) COMPILE_FLAGS "/wd4251") endif() - install (TARGETS ${plugin_name} DESTINATION ${GZ_GUI_PLUGIN_INSTALL_DIR}) + install (TARGETS ${plugin_name} DESTINATION ${GZ_GUI_PLUGIN_RELATIVE_INSTALL_DIR}) endfunction() # Plugins add_subdirectory(camera_fps) add_subdirectory(camera_tracking) +add_subdirectory(camera_tracking_config) add_subdirectory(grid_config) add_subdirectory(image_display) add_subdirectory(interactive_view_control) diff --git a/src/plugins/camera_tracking/CameraTracking.cc b/src/plugins/camera_tracking/CameraTracking.cc index cda9a446a..2001ca411 100644 --- a/src/plugins/camera_tracking/CameraTracking.cc +++ b/src/plugins/camera_tracking/CameraTracking.cc @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2024 Rudis Laboratories LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +18,13 @@ #include #include +#include #include +#include #include +#include +#include #include #include #include @@ -60,6 +65,10 @@ class CameraTracking::Implementation public: bool OnMoveTo(const msgs::StringMsg &_msg, msgs::Boolean &_res); + /// \brief Callback for a track message + /// \param[in] _msg Message is of type CameraTrack. + public: void OnTrackSub(const msgs::CameraTrack &_msg); + /// \brief Callback for a follow request /// \param[in] _msg Request message to set the target to follow. /// \param[in] _res Response data @@ -97,27 +106,42 @@ class CameraTracking::Implementation //// \brief Pointer to the rendering scene public: rendering::ScenePtr scene = nullptr; + /// \brief Target to track + public: std::string selectedTrackTarget; + /// \brief Target to follow - public: std::string followTarget; + public: std::string selectedFollowTarget; - /// \brief Wait for follow target - public: bool followTargetWait = false; + /// \brief Wait for target to track + public: bool selectedTargetWait = false; /// \brief Offset of camera from target being followed - public: math::Vector3d followOffset = math::Vector3d(-5, 0, 3); + public: math::Vector3d followOffset = math::Vector3d(-3, 0, 2); + + /// \brief Offset on target to be tracked + public: math::Vector3d trackOffset = math::Vector3d(0, 0, 0); - /// \brief Flag to indicate the follow offset needs to be updated - public: bool followOffsetDirty = false; + /// \brief Camera tracking message + public: gz::msgs::CameraTrack trackMsg; - /// \brief Flag to indicate the follow offset has been updated - public: bool newFollowOffset = true; + /// \brief Flag to indicate new tracking + public: bool newTrack = true; + + /// \brief Track P gain + public: double trackPGain = 0.01; /// \brief Follow P gain public: double followPGain = 0.01; - /// \brief True follow the target at an offset that is in world frame, - /// false to follow in target's local frame - public: bool followWorldFrame = false; + /// \brief Free Look P gain + public: double freeLookPGain = 1.0; + + /// \brief Default track mode to None + public: int trackMode = gz::msgs::CameraTrack::NONE; + + /// \brief True track the target at an offset that is in world frame, + /// false to track in target's local frame + public: bool trackWorldFrame = false; /// \brief Last move to animation time public: std::chrono::time_point prevMoveToTime; @@ -137,17 +161,23 @@ class CameraTracking::Implementation /// \brief Move to service public: std::string moveToService; + /// \brief Follow service (deprecated) + public: std::string followService; + + /// \brief Follow offset service (deprecated) + public: std::string followOffsetService; + /// \brief The pose set from the move to pose service. public: std::optional moveToPoseValue; /// \brief The motion duration set from the move to pose service. public: std::optional moveToPoseDuration; - /// \brief Follow service - public: std::string followService; + /// \brief Track topic + public: std::string trackTopic; - /// \brief Follow offset service - public: std::string followOffsetService; + /// \brief Track status topic + public: std::string trackStatusTopic; /// \brief Camera pose topic public: std::string cameraPoseTopic; @@ -155,6 +185,9 @@ class CameraTracking::Implementation /// \brief Move to pose service public: std::string moveToPoseService; + /// \brief Camera pose publisher + public: transport::Node::Publisher trackStatusPub; + /// \brief Camera pose publisher public: transport::Node::Publisher cameraPosePub; @@ -196,7 +229,21 @@ void CameraTracking::Implementation::Initialize() this->node.Advertise(this->followService, &Implementation::OnFollow, this); gzmsg << "Follow service on [" - << this->followService << "]" << std::endl; + << this->followService << "] (deprecated)" << std::endl; + + // track + this->trackTopic = "/gui/track"; + this->node.Subscribe(this->trackTopic, + &Implementation::OnTrackSub, this); + gzmsg << "Tracking topic on [" + << this->trackTopic << "]" << std::endl; + + // tracking status + this->trackStatusTopic = "/gui/currently_tracked"; + this->trackStatusPub = + this->node.Advertise(this->trackStatusTopic); + gzmsg << "Tracking status topic on [" + << this->trackStatusTopic << "]" << std::endl; // move to pose service this->moveToPoseService = @@ -218,7 +265,7 @@ void CameraTracking::Implementation::Initialize() this->node.Advertise(this->followOffsetService, &Implementation::OnFollowOffset, this); gzmsg << "Follow offset service on [" - << this->followOffsetService << "]" << std::endl; + << this->followOffsetService << "] (deprecated)" << std::endl; } ///////////////////////////////////////////////// @@ -237,12 +284,60 @@ bool CameraTracking::Implementation::OnFollow(const msgs::StringMsg &_msg, msgs::Boolean &_res) { std::lock_guard lock(this->mutex); - this->followTarget = _msg.data(); + this->selectedFollowTarget = _msg.data(); _res.set_data(true); + + this->trackMode = gz::msgs::CameraTrack::FOLLOW; + + this->newTrack = true; return true; } +///////////////////////////////////////////////// +void CameraTracking::Implementation::OnTrackSub(const msgs::CameraTrack &_msg) +{ + std::lock_guard lock(this->mutex); + gzmsg << "Got new track message." << std::endl; + + if (_msg.track_mode() != gz::msgs::CameraTrack::USE_LAST) + { + this->trackMode = _msg.track_mode(); + } + if (!_msg.follow_target().name().empty()) + { + this->selectedFollowTarget = _msg.follow_target().name(); + } + if (!_msg.track_target().name().empty()) + { + this->selectedTrackTarget = _msg.track_target().name(); + } + if (_msg.follow_target().name().empty() && _msg.track_target().name().empty() + && _msg.track_mode() != gz::msgs::CameraTrack::USE_LAST) + { + gzmsg << "Track and Follow target names empty."<< std::endl; + } + if (_msg.has_follow_offset()) + { + this->followOffset = msgs::Convert(_msg.follow_offset()); + } + if (_msg.has_track_offset()) + { + this->trackOffset = msgs::Convert(_msg.track_offset()); + } + if (_msg.track_pgain() > 0.00001) + { + this->trackPGain = _msg.track_pgain(); + } + if (_msg.follow_pgain() > 0.00001) + { + this->followPGain = _msg.follow_pgain(); + } + + this->newTrack = true; + return; +} + ///////////////////////////////////////////////// void CameraTracking::Implementation::OnMoveToComplete() { @@ -261,9 +356,9 @@ bool CameraTracking::Implementation::OnFollowOffset(const msgs::Vector3d &_msg, msgs::Boolean &_res) { std::lock_guard lock(this->mutex); - if (!this->followTarget.empty()) + if (!this->selectedFollowTarget.empty()) { - this->newFollowOffset = true; + this->newTrack = true; this->followOffset = msgs::Convert(_msg); } @@ -379,66 +474,114 @@ void CameraTracking::Implementation::OnRender() } } - // Follow + // Track { - GZ_PROFILE("CameraTracking::Implementation::OnRender Follow"); - // reset follow mode if target node got removed - if (!this->followTarget.empty()) + GZ_PROFILE("CameraTracking::Implementation::OnRender Track"); + // reset track mode if target node got removed + if (!this->selectedFollowTarget.empty()) { - rendering::NodePtr target = this->scene->NodeByName(this->followTarget); - if (!target && !this->followTargetWait) + rendering::NodePtr targetFollow = this->scene->NodeByName( + this->selectedFollowTarget); + if (!targetFollow && !this->selectedTargetWait) { this->camera->SetFollowTarget(nullptr); + this->selectedFollowTarget.clear(); + } + } + if (!this->selectedTrackTarget.empty()) + { + rendering::NodePtr targetTrack = this->scene->NodeByName( + this->selectedTrackTarget); + if (!targetTrack && !this->selectedTargetWait) + { this->camera->SetTrackTarget(nullptr); - this->followTarget.clear(); + this->selectedTrackTarget.clear(); } } if (!this->moveToTarget.empty()) return; - rendering::NodePtr followTargetTmp = this->camera->FollowTarget(); - if (!this->followTarget.empty()) + rendering::NodePtr selectedFollowTargetTmp = this->camera->FollowTarget(); + rendering::NodePtr selectedTrackTargetTmp = this->camera->TrackTarget(); + if (!this->selectedTrackTarget.empty() || + !this->selectedFollowTarget.empty()) { - rendering::NodePtr target = scene->NodeByName( - this->followTarget); - if (target) + rendering::NodePtr targetFollow = this->scene->NodeByName( + this->selectedFollowTarget); + rendering::NodePtr targetTrack = this->scene->NodeByName( + this->selectedTrackTarget); + if (targetFollow || targetTrack) { - if (!followTargetTmp || target != followTargetTmp - || this->newFollowOffset) + if (this->trackMode == gz::msgs::CameraTrack::FOLLOW_FREE_LOOK || + this->trackMode == gz::msgs::CameraTrack::FOLLOW || + this->trackMode == gz::msgs::CameraTrack::FOLLOW_LOOK_AT ) { - this->camera->SetFollowTarget(target, - this->followOffset, - this->followWorldFrame); - this->camera->SetFollowPGain(this->followPGain); - - this->camera->SetTrackTarget(target); - // found target, no need to wait anymore - this->newFollowOffset = false; - this->followTargetWait = false; + if (!selectedFollowTargetTmp || + targetFollow != selectedFollowTargetTmp || + this->newTrack) + { + this->trackWorldFrame = false; + this->camera->SetFollowTarget(targetFollow, + this->followOffset, + this->trackWorldFrame); + if (this->trackMode == gz::msgs::CameraTrack::FOLLOW) + { + this->camera->SetTrackTarget(targetFollow); + this->camera->SetTrackPGain(this->followPGain); + this->camera->SetFollowPGain(this->trackPGain); + } + if (this->trackMode == gz::msgs::CameraTrack::FOLLOW_LOOK_AT) + { + this->camera->SetTrackTarget(targetTrack); + this->camera->SetTrackPGain(this->followPGain); + this->camera->SetFollowPGain(this->trackPGain); + } + if (this->trackMode == gz::msgs::CameraTrack::FOLLOW_FREE_LOOK) + { + this->camera->SetTrackTarget(nullptr); + this->camera->SetFollowPGain(this->freeLookPGain); + } + this->newTrack = false; + this->selectedTargetWait = false; + } } - else if (this->followOffsetDirty) + if (this->trackMode == gz::msgs::CameraTrack::TRACK) { - math::Vector3d offset = - this->camera->WorldPosition() - target->WorldPosition(); - if (!this->followWorldFrame) + if (!selectedTrackTargetTmp || + targetTrack != selectedTrackTargetTmp || + this->newTrack) { - offset = target->WorldRotation().RotateVectorReverse(offset); + this->trackWorldFrame = true; + this->camera->SetFollowTarget(nullptr); + this->camera->SetTrackTarget(targetTrack, + this->trackOffset, + this->trackWorldFrame); + this->camera->SetTrackPGain(this->trackPGain); + this->newTrack = false; + this->selectedTargetWait = false; } - this->camera->SetFollowOffset(offset); - this->followOffsetDirty = false; } } - else if (!this->followTargetWait) + else if (!this->selectedTargetWait) { + gzerr << "Unable to track target. Target: '" + << this->selectedTrackTarget << "' not found" << std::endl; gzerr << "Unable to follow target. Target: '" - << this->followTarget << "' not found" << std::endl; - this->followTarget.clear(); + << this->selectedFollowTarget << "' not found" << std::endl; + this->selectedFollowTarget.clear(); + this->selectedTrackTarget.clear(); } } - else if (followTargetTmp) + else { - this->camera->SetFollowTarget(nullptr); - this->camera->SetTrackTarget(nullptr); + if (selectedFollowTargetTmp) + { + this->camera->SetFollowTarget(nullptr); + } + if (selectedTrackTargetTmp) + { + this->camera->SetTrackTarget(nullptr); + } } } } @@ -458,6 +601,96 @@ CameraTracking::CameraTracking() auto poseMsg = msgs::Convert(this->dataPtr->camera->WorldPose()); this->dataPtr->cameraPosePub.Publish(poseMsg); } + if (this->dataPtr->trackStatusPub.HasConnections()) + { + if (this->dataPtr->trackMode == gz::msgs::CameraTrack::TRACK) + { + this->dataPtr->trackMsg.set_track_mode(gz::msgs::CameraTrack::TRACK); + this->dataPtr->trackMsg.mutable_track_target()->set_name( + this->dataPtr->selectedTrackTarget); + this->dataPtr->trackMsg.mutable_track_offset()->set_x( + this->dataPtr->trackOffset.X()); + this->dataPtr->trackMsg.mutable_track_offset()->set_y( + this->dataPtr->trackOffset.Y()); + this->dataPtr->trackMsg.mutable_track_offset()->set_z( + this->dataPtr->trackOffset.Z()); + this->dataPtr->trackMsg.set_track_pgain( + this->dataPtr->trackPGain); + this->dataPtr->trackMsg.clear_follow_target(); + this->dataPtr->trackMsg.clear_follow_offset(); + this->dataPtr->trackMsg.clear_follow_pgain(); + } + else if (this->dataPtr->trackMode == gz::msgs::CameraTrack::FOLLOW) + { + this->dataPtr->trackMsg.set_track_mode(gz::msgs::CameraTrack::FOLLOW); + this->dataPtr->trackMsg.mutable_follow_target()->set_name( + this->dataPtr->selectedFollowTarget); + this->dataPtr->trackMsg.mutable_follow_offset()->set_x( + this->dataPtr->followOffset.X()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_y( + this->dataPtr->followOffset.Y()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_z( + this->dataPtr->followOffset.Z()); + this->dataPtr->trackMsg.set_follow_pgain(this->dataPtr->followPGain); + this->dataPtr->trackMsg.clear_track_target(); + this->dataPtr->trackMsg.clear_track_offset(); + this->dataPtr->trackMsg.clear_track_pgain(); + } + else if (this->dataPtr->trackMode == + gz::msgs::CameraTrack::FOLLOW_FREE_LOOK) + { + this->dataPtr->trackMsg.set_track_mode( + gz::msgs::CameraTrack::FOLLOW_FREE_LOOK); + this->dataPtr->trackMsg.mutable_follow_target()->set_name( + this->dataPtr->selectedFollowTarget); + this->dataPtr->trackMsg.mutable_follow_offset()->set_x( + this->dataPtr->followOffset.X()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_y( + this->dataPtr->followOffset.Y()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_z( + this->dataPtr->followOffset.Z()); + this->dataPtr->trackMsg.set_follow_pgain(this->dataPtr->followPGain); + this->dataPtr->trackMsg.clear_track_target(); + this->dataPtr->trackMsg.clear_track_offset(); + this->dataPtr->trackMsg.clear_track_pgain(); + } + else if (this->dataPtr->trackMode == + gz::msgs::CameraTrack::FOLLOW_LOOK_AT) + { + this->dataPtr->trackMsg.set_track_mode( + gz::msgs::CameraTrack::FOLLOW_LOOK_AT); + this->dataPtr->trackMsg.mutable_follow_target()->set_name( + this->dataPtr->selectedFollowTarget); + this->dataPtr->trackMsg.mutable_track_target()->set_name( + this->dataPtr->selectedTrackTarget); + this->dataPtr->trackMsg.mutable_follow_offset()->set_x( + this->dataPtr->followOffset.X()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_y( + this->dataPtr->followOffset.Y()); + this->dataPtr->trackMsg.mutable_follow_offset()->set_z( + this->dataPtr->followOffset.Z()); + this->dataPtr->trackMsg.mutable_track_offset()->set_x( + this->dataPtr->trackOffset.X()); + this->dataPtr->trackMsg.mutable_track_offset()->set_y( + this->dataPtr->trackOffset.Y()); + this->dataPtr->trackMsg.mutable_track_offset()->set_z( + this->dataPtr->trackOffset.Z()); + this->dataPtr->trackMsg.set_follow_pgain(this->dataPtr->followPGain); + this->dataPtr->trackMsg.set_track_pgain(this->dataPtr->trackPGain); + } + else + { + this->dataPtr->trackMsg.set_track_mode(gz::msgs::CameraTrack::NONE); + this->dataPtr->trackMsg.clear_track_target(); + this->dataPtr->trackMsg.clear_track_offset(); + this->dataPtr->trackMsg.clear_track_pgain(); + this->dataPtr->trackMsg.clear_follow_target(); + this->dataPtr->trackMsg.clear_follow_offset(); + this->dataPtr->trackMsg.clear_follow_pgain(); + } + + this->dataPtr->trackStatusPub.Publish(this->dataPtr->trackMsg); + } }); this->dataPtr->timer->setInterval(1000.0 / 50.0); this->dataPtr->timer->start(); @@ -467,11 +700,39 @@ CameraTracking::CameraTracking() CameraTracking::~CameraTracking() = default; ///////////////////////////////////////////////// -void CameraTracking::LoadConfig(const tinyxml2::XMLElement *) +void CameraTracking::LoadConfig(const tinyxml2::XMLElement *_pluginElem) { if (this->title.empty()) this->title = "Camera tracking"; + if (_pluginElem) + { + if (auto followTargetElem = _pluginElem->FirstChildElement("follow_target")) + { + this->dataPtr->selectedFollowTarget = followTargetElem->GetText(); + gzmsg << "CameraTracking: Loaded follow target from sdf [" + << this->dataPtr->selectedFollowTarget << "]" << std::endl; + this->dataPtr->selectedTargetWait = true; + } + if (auto followOffsetElem = _pluginElem->FirstChildElement("follow_offset")) + { + std::stringstream followOffsetStr; + followOffsetStr << std::string(followOffsetElem->GetText()); + followOffsetStr >> this->dataPtr->followOffset; + gzmsg << "CameraTracking: Loaded offset from sdf [" + << this->dataPtr->followOffset << "]" << std::endl; + this->dataPtr->newTrack = true; + } + if (auto followPGainElem = _pluginElem->FirstChildElement("follow_pgain")) + { + this->dataPtr->followPGain = std::stod( + std::string(followPGainElem->GetText())); + gzmsg << "CameraTracking: Loaded follow pgain from sdf [" + << this->dataPtr->followPGain << "]" << std::endl; + this->dataPtr->newTrack = true; + } + } + App()->findChild()->installEventFilter(this); } @@ -481,9 +742,12 @@ void CameraTracking::Implementation::HandleKeyRelease( { if (_e->Key().Key() == Qt::Key_Escape) { - if (!this->followTarget.empty()) + this->trackMode = gz::msgs::CameraTrack::NONE; + if (!this->selectedFollowTarget.empty() || + !this->selectedTrackTarget.empty()) { - this->followTarget = std::string(); + this->selectedFollowTarget = std::string(); + this->selectedTrackTarget = std::string(); _e->accept(); } diff --git a/src/plugins/camera_tracking/CameraTracking.hh b/src/plugins/camera_tracking/CameraTracking.hh index df4601001..2ce6ef92c 100644 --- a/src/plugins/camera_tracking/CameraTracking.hh +++ b/src/plugins/camera_tracking/CameraTracking.hh @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2024 Rudis Laboratories LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,12 +30,14 @@ namespace gz::gui::plugins /// and "follow". /// /// Services: + /// * `/gui/follow`: Set the user camera to follow a given target, + /// identified by name (deprecated). + /// * `/gui/follow/offset`: Set the offset for following (deprecated). /// * `/gui/move_to`: Move the user camera to look at a given target, /// identified by name. /// * `/gui/move_to/pose`: Move the user camera to a given pose. - /// * `/gui/follow`: Set the user camera to follow a given target, - /// identified by name. - /// * `/gui/follow/offset`: Set the offset for following. + /// * `/gui/track`: Set the user camera to follow a given target, + /// identified by name, offset, pgain, track type. /// /// Topics: /// * `/gui/camera/pose`: Publishes the current user camera pose. diff --git a/src/plugins/camera_tracking/CameraTracking.qml b/src/plugins/camera_tracking/CameraTracking.qml index 09c339f0b..a8c82d45e 100644 --- a/src/plugins/camera_tracking/CameraTracking.qml +++ b/src/plugins/camera_tracking/CameraTracking.qml @@ -1,5 +1,6 @@ /* * Copyright (C) 2021 Open Source Robotics Foundation + * Copyright (C) 2024 Rudis Laboratories LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +27,12 @@ ColumnLayout { anchors.margins: 10 property string message: 'Services provided:
    ' + + '
  • /gui/follow/pose
  • ' + '
  • /gui/move_to
  • ' + '
  • /gui/move_to/pose
  • ' + - '
  • /gui/follow
  • ' + - '
  • /gui/follow/offset

Topics provided:
    ' + - '
  • /gui/camera/pose
' + '
  • /gui/track

  • Topics provided:
      ' + + '
    • /gui/camera/pose
    • '+ + '
    • /gui/currently_tracked
    ' Label { Layout.fillWidth: true diff --git a/src/plugins/camera_tracking_config/CMakeLists.txt b/src/plugins/camera_tracking_config/CMakeLists.txt new file mode 100644 index 000000000..b6877357c --- /dev/null +++ b/src/plugins/camera_tracking_config/CMakeLists.txt @@ -0,0 +1,6 @@ +gz_gui_add_plugin(CameraTrackingConfig + SOURCES + CameraTrackingConfig.cc + QT_HEADERS + CameraTrackingConfig.hh +) diff --git a/src/plugins/camera_tracking_config/CameraTrackingConfig.cc b/src/plugins/camera_tracking_config/CameraTrackingConfig.cc new file mode 100644 index 000000000..c61f8ac44 --- /dev/null +++ b/src/plugins/camera_tracking_config/CameraTrackingConfig.cc @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2024 CogniPilot Foundation + * Copyright (C) 2024 Rudis Laboratories LLC + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include "gz/gui/Application.hh" +#include "gz/gui/Conversions.hh" +#include "gz/gui/GuiEvents.hh" +#include "gz/gui/MainWindow.hh" + +#include + +#include "CameraTrackingConfig.hh" + +/// \brief Private data class for CameraTrackingConfig +class gz::gui::plugins::CameraTrackingConfigPrivate +{ + + /// \brief Topic for track message + public: std::string cameraTrackingTopic; + + /// \brief tracking offset + public: math::Vector3d trackOffset{math::Vector3d(0.0, 0.0, 0.0)}; + + /// \brief track P gain + public: double trackPGain{0.01}; + + /// \brief Offset of camera from target being followed + public: math::Vector3d followOffset{math::Vector3d(-3.0, 0.0, -2.0)}; + + /// \brief Follow P gain + public: double followPGain{0.01}; + + public: transport::Node node; + + /// \brief Process updated track + public: void UpdateTracking(); + + /// \brief flag for updating + public: bool newTrackingUpdate = false; + + /// \brief track publisher + public: transport::Node::Publisher trackingPub; +}; + +using namespace gz; +using namespace gui; +using namespace plugins; + +///////////////////////////////////////////////// +CameraTrackingConfig::CameraTrackingConfig() + : gz::gui::Plugin(), dataPtr(std::make_unique()) +{ +} + +///////////////////////////////////////////////// +CameraTrackingConfig::~CameraTrackingConfig() = default; + +///////////////////////////////////////////////// +void CameraTrackingConfig::LoadConfig(const tinyxml2::XMLElement *) +{ + if (this->title.empty()) + this->title = "Camera Tracking Config"; + + // Track target pose service + this->dataPtr->cameraTrackingTopic = "/gui/track"; + this->dataPtr->trackingPub = + this->dataPtr->node.Advertise( + this->dataPtr->cameraTrackingTopic); + gzmsg << "CameraTrackingConfig: Tracking topic publisher advertised on [" + << this->dataPtr->cameraTrackingTopic << "]" << std::endl; + + gui::App()->findChild< + MainWindow *>()->installEventFilter(this); +} + +///////////////////////////////////////////////// +bool CameraTrackingConfig::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == events::Render::kType) + { + if (this->dataPtr->newTrackingUpdate) + { + this->dataPtr->UpdateTracking(); + } + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} + +///////////////////////////////////////////////// +void CameraTrackingConfig::SetTracking( + double _tx, double _ty, double _tz, double _tp, + double _fx, double _fy, double _fz, double _fp) +{ + if (!this->dataPtr->newTrackingUpdate) + { + this->dataPtr->trackOffset = math::Vector3d( + _tx, _ty, _tz); + this->dataPtr->followOffset = math::Vector3d( + _fx, _fy, _fz); + this->dataPtr->trackPGain = _tp; + this->dataPtr->followPGain = _fp; + gzmsg << "CameraTrackingConfig: Track: Offset(" + << this->dataPtr->trackOffset << "), PGain(" + << this->dataPtr->trackPGain << ")" << std::endl; + gzmsg << "CameraTrackingConfig: Follow: Offset(" + << this->dataPtr->followOffset << "), PGain(" + << this->dataPtr->followPGain << ")" << std::endl; + this->dataPtr->newTrackingUpdate = true; + } +} + +///////////////////////////////////////////////// +void CameraTrackingConfigPrivate::UpdateTracking() +{ + // Track + msgs::CameraTrack trackingMsg; + trackingMsg.set_track_mode(msgs::CameraTrack::USE_LAST); + trackingMsg.mutable_track_offset()->set_x(this->trackOffset.X()); + trackingMsg.mutable_track_offset()->set_y(this->trackOffset.Y()); + trackingMsg.mutable_track_offset()->set_z(this->trackOffset.Z()); + trackingMsg.mutable_follow_offset()->set_x(this->followOffset.X()); + trackingMsg.mutable_follow_offset()->set_y(this->followOffset.Y()); + trackingMsg.mutable_follow_offset()->set_z(this->followOffset.Z()); + trackingMsg.set_follow_pgain(this->followPGain); + trackingMsg.set_track_pgain(this->trackPGain); + + this->trackingPub.Publish(trackingMsg); + gzmsg << "CameraTrackingConfig: Publishing message." << std::endl; + this->newTrackingUpdate = false; +} + +// Register this plugin +GZ_ADD_PLUGIN(CameraTrackingConfig, + gui::Plugin) diff --git a/src/plugins/camera_tracking_config/CameraTrackingConfig.hh b/src/plugins/camera_tracking_config/CameraTrackingConfig.hh new file mode 100644 index 000000000..dca58a52e --- /dev/null +++ b/src/plugins/camera_tracking_config/CameraTrackingConfig.hh @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2024 CogniPilot Foundation + * Copyright (C) 2024 Rudis Laboratories LLC + * + * 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 GZ_GUI_PLUGINS_CAMERATRACKINGCONFIG_HH_ +#define GZ_GUI_PLUGINS_CAMERATRACKINGCONFIG_HH_ + +#include +#include +#include + +#include "gz/gui/Plugin.hh" + +namespace gz +{ +namespace gui +{ +namespace plugins +{ + class CameraTrackingConfigPrivate; + + class CameraTrackingConfig : public Plugin + { + Q_OBJECT + + /// \brief Constructor + public: CameraTrackingConfig(); + + /// \brief Destructor + public: virtual ~CameraTrackingConfig(); + + // Documentation inherited + public: virtual void LoadConfig(const tinyxml2::XMLElement *) + override; + + /// \brief Set the tracking camera, requested from the GUI. + /// \param[in] _tx The track offset in x + /// \param[in] _ty The track offset in y + /// \param[in] _tz The track offset in z + /// \param[in] _tp The track camera P gain + /// \param[in] _fx The follow offset in x + /// \param[in] _fy The follow offset in y + /// \param[in] _fz The follow offset in z + /// \param[in] _fp The follow camera P gain + public slots: void SetTracking( + double _tx, double _ty, double _tz, double _tp, + double _fx, double _fy, double _fz, double _fp); + + // Documentation inherited + private: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \internal + /// \brief Pointer to private data. + private: std::unique_ptr dataPtr; + }; +} +} +} +#endif diff --git a/src/plugins/camera_tracking_config/CameraTrackingConfig.qml b/src/plugins/camera_tracking_config/CameraTrackingConfig.qml new file mode 100644 index 000000000..91c5e144a --- /dev/null +++ b/src/plugins/camera_tracking_config/CameraTrackingConfig.qml @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2024 CogniPilot Foundation + * Copyright (C) 2024 Rudis Laboratories LLC + * + * 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. + * +*/ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Controls.Material 2.1 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Layouts 1.3 +import gz.gui 1.0 + +ColumnLayout { + Layout.minimumWidth: 200 + Layout.minimumHeight: 200 + Layout.margins: 2 + anchors.fill: parent + focus: true + + // X track camera pose + property double xTrackPose: 0.0 + // Y track camera pose + property double yTrackPose: 0.0 + // Z track camera pose + property double zTrackPose: 0.0 + // P Gain track camera pose + property double pGainTrack: 0.01 + // X camera follow distance + property double xFollowOffset: -3.0 + // Y camera follow distance + property double yFollowOffset: 0.0 + // Z camera follow distance + property double zFollowOffset: 2.0 + // P Gain camera follow distance + property double pGainFollow: 0.01 + + GridLayout { + Layout.fillWidth: true + Layout.margins: 2 + columns: 2 + + // X Track Offset + Label { + id: xTrackPoseLabel + text: "Track Offset X (m)" + color: "dimgrey" + } + GzSpinBox { + id: xTrackPoseField + Layout.fillWidth: true + value: xTrackPose + maximumValue: 1000.0 + minimumValue: -1000.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + xTrackPose = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset, pGainFollow) + } + } + // Y Track Offset + Label { + id: yTrackPoseLabel + text: "Track Offset Y (m)" + color: "dimgrey" + } + GzSpinBox { + id: yTrackPoseField + Layout.fillWidth: true + value: yTrackPose + maximumValue: 1000.0 + minimumValue: -1000.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + yTrackPose = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset, pGainFollow) + } + } + // Z Track Offset + Label { + id: zTrackPoseLabel + text: "Track Offset Z (m)" + color: "dimgrey" + } + GzSpinBox { + id: zTrackPoseField + Layout.fillWidth: true + value: zTrackPose + maximumValue: 1000.0 + minimumValue: -1000.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + zTrackPose = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset, pGainFollow) + } + } + // P Gain track + Label { + id: pGainTrackLabel + text: "Track P Gain" + color: "dimgrey" + } + GzSpinBox { + id: pGainTrackField + Layout.fillWidth: true + value: pGainTrack + maximumValue: 1.0 + minimumValue: 0.001 + decimals: 3 + stepSize: 0.01 + onEditingFinished:{ + pGainTrack = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset, pGainFollow) + } + } + // X Follow Offset + Label { + id: xFollowOffsetLabel + text: "Follow Offset X (m)" + color: "dimgrey" + } + GzSpinBox { + id: xFollowOffsetField + Layout.fillWidth: true + value: xFollowOffset + maximumValue: 100.0 + minimumValue: -100.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + xFollowOffset = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset,pGainFollow) + } + } + // Y Follow Offset + Label { + id: yFollowOffsetLabel + text: "Follow Offset Y (m)" + color: "dimgrey" + } + GzSpinBox { + id: yFollowOffsetField + Layout.fillWidth: true + value: yFollowOffset + maximumValue: 100.0 + minimumValue: -100.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + yFollowOffset = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset,pGainFollow) + } + } + // Z Follow Offset + Label { + id: zFollowOffsetLabel + text: "Follow Offset Z (m)" + color: "dimgrey" + } + GzSpinBox { + id: zFollowOffsetField + Layout.fillWidth: true + value: zFollowOffset + maximumValue: 100.0 + minimumValue: -100.0 + decimals: 2 + stepSize: 0.5 + onEditingFinished:{ + zFollowOffset = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset,pGainFollow) + } + } + // P Gain follow + Label { + id: pGainFollowLabel + text: "Follow Offset P Gain" + color: "dimgrey" + } + GzSpinBox { + id: pGainFollowField + Layout.fillWidth: true + value: pGainFollow + maximumValue: 1.0 + minimumValue: 0.001 + decimals: 3 + stepSize: 0.01 + onEditingFinished:{ + pGainFollow = value + CameraTrackingConfig.SetTracking(xTrackPose, yTrackPose, zTrackPose, pGainTrack, xFollowOffset, yFollowOffset, zFollowOffset,pGainFollow) + } + } + } +} diff --git a/src/plugins/camera_tracking_config/CameraTrackingConfig.qrc b/src/plugins/camera_tracking_config/CameraTrackingConfig.qrc new file mode 100644 index 000000000..5b9e0d08d --- /dev/null +++ b/src/plugins/camera_tracking_config/CameraTrackingConfig.qrc @@ -0,0 +1,5 @@ + + + CameraTrackingConfig.qml + + diff --git a/src/plugins/grid_config/CMakeLists.txt b/src/plugins/grid_config/CMakeLists.txt index 101e6edee..5c2a3cb65 100644 --- a/src/plugins/grid_config/CMakeLists.txt +++ b/src/plugins/grid_config/CMakeLists.txt @@ -11,4 +11,4 @@ gz_gui_add_plugin(GridConfig install ( FILES $ RENAME ${CMAKE_SHARED_LIBRARY_PREFIX}Grid3D${CMAKE_SHARED_LIBRARY_SUFFIX} - DESTINATION ${GZ_GUI_PLUGIN_INSTALL_DIR}) + DESTINATION ${GZ_GUI_PLUGIN_RELATIVE_INSTALL_DIR}) diff --git a/src/plugins/image_display/ImageDisplay.cc b/src/plugins/image_display/ImageDisplay.cc index d0a090d49..404c48a7b 100644 --- a/src/plugins/image_display/ImageDisplay.cc +++ b/src/plugins/image_display/ImageDisplay.cc @@ -121,7 +121,8 @@ void ImageDisplay::ProcessImage() case msgs::PixelFormatType::RGB_INT8: // copy image data buffer directly to QImage image = QImage(reinterpret_cast( - this->dataPtr->imageMsg.data().c_str()), width, height, qFormat); + this->dataPtr->imageMsg.data().c_str()), width, height, + 3 * width, qFormat); break; // for other cases, convert to RGB common::Image case msgs::PixelFormatType::R_FLOAT32: diff --git a/src/plugins/minimal_scene/EngineToQtInterface.cc b/src/plugins/minimal_scene/EngineToQtInterface.cc index 8e82576ff..380f881f4 100644 --- a/src/plugins/minimal_scene/EngineToQtInterface.cc +++ b/src/plugins/minimal_scene/EngineToQtInterface.cc @@ -136,7 +136,13 @@ GLuint EngineToQtInterface::TextureId(gz::rendering::CameraPtr &_camera) { if (!this->NeedsFallback(_camera)) { - return _camera->RenderTextureGLId(); + auto textureId = _camera->RenderTextureGLId(); + + QOpenGLFunctions *glFuncs = this->dataPtr->glContext->functions(); + glFuncs->glBindTexture(GL_TEXTURE_2D, textureId); + glFuncs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SRGB_DECODE_EXT, + GL_SKIP_DECODE_EXT); + return textureId; } else { diff --git a/src/plugins/minimal_scene/MinimalScene.cc b/src/plugins/minimal_scene/MinimalScene.cc index 2405b56d2..720854306 100644 --- a/src/plugins/minimal_scene/MinimalScene.cc +++ b/src/plugins/minimal_scene/MinimalScene.cc @@ -1514,12 +1514,6 @@ void MinimalScene::LoadConfig(const tinyxml2::XMLElement *_pluginElem) } renderWindow->SetEngineName(cmdRenderEngine); - // there is a problem with displaying ogre2 render textures that are in - // sRGB format. Workaround for now is to apply gamma correction - // manually. - // There maybe a better way to solve the problem by making OpenGL calls. - if (cmdRenderEngine == std::string("ogre2")) - this->PluginItem()->setProperty("gammaCorrect", true); } ///////////////////////////////////////////////// diff --git a/src/plugins/minimal_scene/MinimalScene.qml b/src/plugins/minimal_scene/MinimalScene.qml index 5969a31c6..62919ec79 100644 --- a/src/plugins/minimal_scene/MinimalScene.qml +++ b/src/plugins/minimal_scene/MinimalScene.qml @@ -25,11 +25,6 @@ Rectangle { Layout.minimumHeight: 200 anchors.fill: parent - /** - * True to enable gamma correction - */ - property bool gammaCorrect: false - /** * Get mouse position on 3D widget */ @@ -54,16 +49,6 @@ Rectangle { visible: MinimalScene.loadingError.length == 0 } - /* - * Gamma correction for sRGB output. Enabled when engine is set to ogre2 - */ - GammaAdjust { - anchors.fill: renderWindow - source: renderWindow - gamma: 2.4 - enabled: gammaCorrect - visible: gammaCorrect - } onParentChanged: { if (undefined === parent)