diff --git a/docs/assets/viewer_3d/open_3d_viewer.jpg b/docs/assets/viewer_3d/open_3d_viewer.jpg new file mode 100644 index 00000000000..16569ab636a Binary files /dev/null and b/docs/assets/viewer_3d/open_3d_viewer.jpg differ diff --git a/docs/en/qgc-dev-guide/getting_started/index.md b/docs/en/qgc-dev-guide/getting_started/index.md index e92ffde4ad6..db0241d6711 100644 --- a/docs/en/qgc-dev-guide/getting_started/index.md +++ b/docs/en/qgc-dev-guide/getting_started/index.md @@ -78,6 +78,10 @@ To install Qt: Then install the following components: +::: info +To see a complete list of all available components in the installer _Select Components_ dialog, you might need to check the **"Archive"** box in the right column under the **"Categories"** tab, then click on **"Filter"**. +::: + - Under _Qt _{{ $frontmatter.qt_version }}_ select: - Depending on the OS you want to build for: - **Windows**: _MSVC 2019 64 bit_ diff --git a/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md b/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md index 8aba720ce4e..58b333f315b 100644 --- a/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md +++ b/docs/en/qgc-user-guide/viewer_3d/viewer_3d.md @@ -16,11 +16,24 @@ You can use it to: ![3D View](../../../assets/viewer_3d/viewer_3d_overview.jpg) # UI Overview -The screenshot above shows the main elements of the 3D View. You can navigate through the 3D View by using the mouse as follows: -- **To move horizontally and vertically**: Press and hold mouse left click, then move the cursor. -- **To rotate**: Press and hold mouse right click, then move the cursor. -- **To zoom**: Use the mouse wheel\middle button. +The screenshot above shows the main elements of the 3D View. +To open the 3D View, when you are in the [Fly View](../fly_view/fly_view.md), from the toolbar on the left, select the 3D View icon as shown below: + +![3D View](../../../assets/viewer_3d/open_3d_viewer.jpg) + +Once the 3D View is opened, you can navigate through the 3D environment by using either a mouse or a touchscreen as follows: +- **Mouse:** + - **To move horizontally and vertically**: Press and hold the mouse left-click, then move the cursor. + - **To rotate**: Press and hold the mouse right-click, then move the cursor. + - **To zoom**: Use the mouse wheel\middle button. + +- **Touchscreen:** + - **To move horizontally and vertically**: Use a single finger, then tap and move your finger. + - **To rotate**: Use two fingers, then tap and move your fingers while keeping them together. + - **To zoom**: Use a pinch with two fingers and move them together or apart to zoom in or out. + +To visualize the 3D map of a particular area in the 3D viewer, you have to download the .osm file of that area from the [OpenStreetMap](https://www.openstreetmap.org/#map=16/47.3964/8.5498) website and then import it through the Setting menu. More details on the Setting menu can be found in the next section. # Setting icon ![Setting icons](../../../assets/viewer_3d/icon_3d_view.jpg) This icon will open a pop-up window for setting up the 3D View as shown below: diff --git a/qgcimages.qrc b/qgcimages.qrc index 5a1d3340472..8c071fe5526 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -207,7 +207,6 @@ src/ui/toolbar/Images/Yield.svg src/FlightMap/Images/ZoomMinus.svg src/FlightMap/Images/ZoomPlus.svg - src/Viewer3D/Images/gear_icon.png src/Viewer3D/Images/city_3d_map_icon.svg diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index de1435f8505..daa1cc8275e 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -315,7 +315,6 @@ src/PlanView/VTOLLandingPatternEditor.qml src/comm/MockLinkOptionsDlg.qml src/FlightDisplay/FlyViewInsetViewer.qml - src/Viewer3D/Viewer3D/Viewer3DSettingMenu.qml src/Viewer3D/Viewer3D/Viewer3D.qml src/Viewer3D/Viewer3D/qmldir src/Viewer3D/Viewer3D/Models3D/CameraLightModel.qml diff --git a/src/FlightDisplay/FlyView.qml b/src/FlightDisplay/FlyView.qml index 9092014341b..a956f605206 100644 --- a/src/FlightDisplay/FlyView.qml +++ b/src/FlightDisplay/FlyView.qml @@ -165,6 +165,7 @@ Item { pipMode: !_mainWindowIsMap toolInsets: customOverlay.totalToolInsets mapName: "FlightDisplayView" + enabled: !viewer3DWindow.isOpen } FlyViewVideo { diff --git a/src/FlightDisplay/FlyViewToolStripActionList.qml b/src/FlightDisplay/FlyViewToolStripActionList.qml index 367d0700544..25fec526325 100644 --- a/src/FlightDisplay/FlyViewToolStripActionList.qml +++ b/src/FlightDisplay/FlyViewToolStripActionList.qml @@ -16,6 +16,7 @@ ToolStripActionList { id: _root signal displayPreFlightChecklist + property bool _viewer3DEnabled: QGroundControl.settingsManager.viewer3DSettings.enabled.rawValue model: [ ToolStripAction { @@ -23,50 +24,33 @@ ToolStripActionList { iconSource: "/qmlimages/Plan.svg" onTriggered:{ mainWindow.showPlanView() - map_icon.showFlyMap() + mapIcon.showFlyMap() } }, ToolStripAction { - id: map_icon + property bool _is3DViewOpen: viewer3DWindow.isOpen + + id: mapIcon + visible: _viewer3DEnabled text: qsTr("3D View") iconSource: "/qmlimages/Viewer3D/City3DMapIcon.svg" onTriggered:{ - if(viewer3DWindow.viewer3DOpen === false) - { - show3dMap(); - } - else - { - showFlyMap(); + if(_is3DViewOpen === false){ + viewer3DWindow.open() + }else{ + viewer3DWindow.close() } } - function show3dMap() - { - viewer3DWindow.viewer3DOpen = true - map_icon.iconSource = "/qmlimages/PaperPlane.svg" - text= qsTr("Fly") - city_map_setting_icon.enabled = true - } - - function showFlyMap() - { - viewer3DWindow.viewer3DOpen = false - iconSource = "/qmlimages/Viewer3D/City3DMapIcon.svg" - text = qsTr("3D View") - city_map_setting_icon.enabled = false - viewer3DWindow.settingMenuOpen = false - city_map_setting_icon.checked = false - } - }, - ToolStripAction { - id: city_map_setting_icon - text: qsTr("Setting") - iconSource: "/qmlimages/Viewer3D/GearIcon.png" - enabled: false - visible: enabled - onTriggered:{ - viewer3DWindow.settingMenuOpen = !viewer3DWindow.settingMenuOpen + on_Is3DViewOpenChanged: { + if(_is3DViewOpen === true){ + mapIcon.iconSource = "/qmlimages/PaperPlane.svg" + text= qsTr("Fly") + }else{ + viewer3DWindow.close() + iconSource = "/qmlimages/Viewer3D/City3DMapIcon.svg" + text = qsTr("3D View") + } } }, PreFlightCheckListShowAction { onTriggered: displayPreFlightChecklist() }, diff --git a/src/Settings/SettingsManager.cc b/src/Settings/SettingsManager.cc index 1f4366d814c..488913e832d 100644 --- a/src/Settings/SettingsManager.cc +++ b/src/Settings/SettingsManager.cc @@ -29,6 +29,7 @@ SettingsManager::SettingsManager(QGCApplication* app, QGCToolbox* toolbox) , _adsbVehicleManagerSettings (nullptr) , _batteryIndicatorSettings (nullptr) , _mapsSettings (nullptr) + , _viewer3DSettings (nullptr) #if !defined(NO_ARDUPILOT_DIALECT) , _apmMavlinkStreamRateSettings (nullptr) #endif @@ -58,6 +59,7 @@ void SettingsManager::setToolbox(QGCToolbox *toolbox) _adsbVehicleManagerSettings = new ADSBVehicleManagerSettings (this); _batteryIndicatorSettings = new BatteryIndicatorSettings (this); _mapsSettings = new MapsSettings (this); + _viewer3DSettings = new Viewer3DSettings (this); #if !defined(NO_ARDUPILOT_DIALECT) _apmMavlinkStreamRateSettings = new APMMavlinkStreamRateSettings(this); #endif diff --git a/src/Settings/SettingsManager.h b/src/Settings/SettingsManager.h index b44f93217da..fc089213452 100644 --- a/src/Settings/SettingsManager.h +++ b/src/Settings/SettingsManager.h @@ -32,6 +32,7 @@ #include "BatteryIndicatorSettings.h" #include #include "RemoteIDSettings.h" +#include "Viewer3DSettings.h" /// Provides access to all app settings class SettingsManager : public QGCTool @@ -56,6 +57,7 @@ class SettingsManager : public QGCTool Q_PROPERTY(QObject* adsbVehicleManagerSettings READ adsbVehicleManagerSettings CONSTANT) Q_PROPERTY(QObject* batteryIndicatorSettings READ batteryIndicatorSettings CONSTANT) Q_PROPERTY(QObject* mapsSettings READ mapsSettings CONSTANT) + Q_PROPERTY(QObject* viewer3DSettings READ viewer3DSettings CONSTANT) #if !defined(NO_ARDUPILOT_DIALECT) Q_PROPERTY(QObject* apmMavlinkStreamRateSettings READ apmMavlinkStreamRateSettings CONSTANT) #endif @@ -78,6 +80,7 @@ class SettingsManager : public QGCTool ADSBVehicleManagerSettings* adsbVehicleManagerSettings (void) { return _adsbVehicleManagerSettings; } BatteryIndicatorSettings* batteryIndicatorSettings (void) { return _batteryIndicatorSettings; } MapsSettings* mapsSettings (void) { return _mapsSettings; } + Viewer3DSettings* viewer3DSettings (void) { return _viewer3DSettings; } #if !defined(NO_ARDUPILOT_DIALECT) APMMavlinkStreamRateSettings* apmMavlinkStreamRateSettings(void) { return _apmMavlinkStreamRateSettings; } #endif @@ -98,6 +101,7 @@ class SettingsManager : public QGCTool ADSBVehicleManagerSettings* _adsbVehicleManagerSettings; BatteryIndicatorSettings* _batteryIndicatorSettings; MapsSettings* _mapsSettings; + Viewer3DSettings* _viewer3DSettings; #if !defined(NO_ARDUPILOT_DIALECT) APMMavlinkStreamRateSettings* _apmMavlinkStreamRateSettings; #endif diff --git a/src/Settings/Viewer3D.SettingsGroup.json b/src/Settings/Viewer3D.SettingsGroup.json index e9523742a9c..6aa548f5c71 100644 --- a/src/Settings/Viewer3D.SettingsGroup.json +++ b/src/Settings/Viewer3D.SettingsGroup.json @@ -3,6 +3,12 @@ "fileType": "FactMetaData", "QGC.MetaData.Facts": [ +{ + "name": "enabled", + "shortDesc": "Enable the 3D viewer", + "type": "bool", + "default": false +}, { "name": "osmFilePath", "shortDesc": "Path to the OSM file for the 3D viewer.", diff --git a/src/Settings/Viewer3DSettings.cc b/src/Settings/Viewer3DSettings.cc index 86d2eda5f2d..1583f973ae1 100644 --- a/src/Settings/Viewer3DSettings.cc +++ b/src/Settings/Viewer3DSettings.cc @@ -17,6 +17,7 @@ DECLARE_SETTINGGROUP(Viewer3D, "Viewer3D") qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "Viewer3DSettings", "Reference only"); } +DECLARE_SETTINGSFACT(Viewer3DSettings, enabled) DECLARE_SETTINGSFACT(Viewer3DSettings, osmFilePath) DECLARE_SETTINGSFACT(Viewer3DSettings, buildingLevelHeight) DECLARE_SETTINGSFACT(Viewer3DSettings, altitudeBias) diff --git a/src/Settings/Viewer3DSettings.h b/src/Settings/Viewer3DSettings.h index 8a202ffd984..c112d5d8174 100644 --- a/src/Settings/Viewer3DSettings.h +++ b/src/Settings/Viewer3DSettings.h @@ -21,6 +21,7 @@ class Viewer3DSettings : public SettingsGroup DEFINE_SETTING_NAME_GROUP() + DEFINE_SETTINGFACT(enabled) DEFINE_SETTINGFACT(osmFilePath) DEFINE_SETTINGFACT(buildingLevelHeight) DEFINE_SETTINGFACT(altitudeBias) diff --git a/src/Viewer3D/CityMapGeometry.cc b/src/Viewer3D/CityMapGeometry.cc index 235846459ee..d31b04ee227 100644 --- a/src/Viewer3D/CityMapGeometry.cc +++ b/src/Viewer3D/CityMapGeometry.cc @@ -2,6 +2,9 @@ #include +#include "QGCApplication.h" +#include "SettingsManager.h" + CityMapGeometry::CityMapGeometry() { @@ -10,8 +13,10 @@ CityMapGeometry::CityMapGeometry() _vertexData.clear(); _mapLoadedFlag = 0; - connect(this, &CityMapGeometry::osmFilePathChanged, this, &CityMapGeometry::updateData); - connect(this, &CityMapGeometry::osmParserChanged, this, &CityMapGeometry::updateData); + _viewer3DSettings = qgcApp()->toolbox()->settingsManager()->viewer3DSettings(); + + setOsmFilePath(_viewer3DSettings->osmFilePath()->rawValue()); + connect(_viewer3DSettings->osmFilePath(), &Fact::rawValueChanged, this, &CityMapGeometry::setOsmFilePath); } void CityMapGeometry::setModelName(QString modelName) @@ -22,17 +27,17 @@ void CityMapGeometry::setModelName(QString modelName) emit modelNameChanged(); } -void CityMapGeometry::setOsmFilePath(QString filePath) +void CityMapGeometry::setOsmFilePath(QVariant value) { - if(_osmFilePath.compare(filePath) == 0){ + if(_osmFilePath.compare(value.toString()) == 0){ return; } + clearViewer(); _mapLoadedFlag = 0; - _osmFilePath = filePath; + _osmFilePath = value.toString(); emit osmFilePathChanged(); - - updateData(); + loadOsmMap(); } void CityMapGeometry::setOsmParser(OsmParser *newOsmParser) @@ -40,26 +45,36 @@ void CityMapGeometry::setOsmParser(OsmParser *newOsmParser) _osmParser = newOsmParser; if(_osmParser){ - connect(_osmParser, &OsmParser::buildingLevelHeightChanged, this, &CityMapGeometry::updateData); + connect(_osmParser, &OsmParser::buildingLevelHeightChanged, this, &CityMapGeometry::updateViewer); + connect(_osmParser, &OsmParser::mapChanged, this, &CityMapGeometry::updateViewer); } emit osmParserChanged(); + loadOsmMap(); } -//! [update data] -void CityMapGeometry::updateData() +bool CityMapGeometry::loadOsmMap() { - clear(); + if(_mapLoadedFlag){ + return true; + } if(!_osmParser){ - return; + return false; } + _mapLoadedFlag = 1; + _osmParser->parseOsmFile(_osmFilePath); + return true; +} + +void CityMapGeometry::updateViewer() +{ + clear(); - if(_mapLoadedFlag == 0){ - _osmParser->parseOsmFile(_osmFilePath); - _mapLoadedFlag = 1; + if(!_osmParser){ + return; } - if(_mapLoadedFlag){ + if(loadOsmMap()){ _vertexData = _osmParser->buildingToMesh(); int stride = 3 * sizeof(float); @@ -72,8 +87,14 @@ void CityMapGeometry::updateData() addAttribute(QQuick3DGeometry::Attribute::PositionSemantic, 0, QQuick3DGeometry::Attribute::F32Type); - } update(); } } + +void CityMapGeometry::clearViewer() +{ + clear(); + _vertexData.clear(); + update(); +} diff --git a/src/Viewer3D/CityMapGeometry.h b/src/Viewer3D/CityMapGeometry.h index b549c94eff2..558a82bfb50 100644 --- a/src/Viewer3D/CityMapGeometry.h +++ b/src/Viewer3D/CityMapGeometry.h @@ -9,11 +9,12 @@ /// @author Omid Esrafilian +class Viewer3DSettings; + class CityMapGeometry : public QQuick3DGeometry { Q_OBJECT Q_PROPERTY(QString modelName READ modelName WRITE setModelName NOTIFY modelNameChanged) - Q_PROPERTY(QString osmFilePath READ osmFilePath WRITE setOsmFilePath NOTIFY osmFilePathChanged) Q_PROPERTY(OsmParser* osmParser READ osmParser WRITE setOsmParser NOTIFY osmParserChanged) public: @@ -24,25 +25,32 @@ class CityMapGeometry : public QQuick3DGeometry void setModelName(QString modelName); QString osmFilePath() const {return _osmFilePath;} - void setOsmFilePath(QString filePath); OsmParser* osmParser(){ return _osmParser;} void setOsmParser(OsmParser* newOsmParser); + bool loadOsmMap(); + signals: void modelNameChanged(); void osmFilePathChanged(); - void gpsRefChanged(); void osmParserChanged(); private: - void updateData(); + void updateViewer(); + void clearViewer(); QString _modelName; QString _osmFilePath; QByteArray _vertexData; OsmParser *_osmParser; bool _mapLoadedFlag; + Viewer3DSettings* _viewer3DSettings = nullptr; + +private slots: + void setOsmFilePath(QVariant value); + + }; #endif // CITYMAPGEOMETRY_H diff --git a/src/Viewer3D/Images/gear_icon.png b/src/Viewer3D/Images/gear_icon.png deleted file mode 100644 index 5ba6043e904..00000000000 Binary files a/src/Viewer3D/Images/gear_icon.png and /dev/null differ diff --git a/src/Viewer3D/OsmParser.cc b/src/Viewer3D/OsmParser.cc index 46fbcd0f771..46f7048b6d5 100644 --- a/src/Viewer3D/OsmParser.cc +++ b/src/Viewer3D/OsmParser.cc @@ -4,7 +4,8 @@ #include "earcut.hpp" #include "Viewer3DUtils.h" - +#include "QGCApplication.h" +#include "SettingsManager.h" typedef union { uint array[3]; @@ -19,22 +20,51 @@ typedef union { OsmParser::OsmParser(QObject *parent) : QObject{parent} { - _mainThread = new QThread(this); + _viewer3DSettings = qgcApp()->toolbox()->settingsManager()->viewer3DSettings(); - this->moveToThread(_mainThread); _gpsRefSet = false; - _buildingLevelHeight = 0; // meters + _mapLoadedFlag = false; + + setBuildingLevelHeight(_viewer3DSettings->buildingLevelHeight()->rawValue()); // meters + connect(_viewer3DSettings->buildingLevelHeight(), &Fact::rawValueChanged, this, &OsmParser::setBuildingLevelHeight); } void OsmParser::setGpsRef(QGeoCoordinate gpsRef) { _gpsRefPoint = gpsRef; _gpsRefSet = true; - emit gpsRefChanged(_gpsRefPoint); + emit gpsRefChanged(_gpsRefPoint, _gpsRefSet); +} + +void OsmParser::resetGpsRef() +{ + _gpsRefPoint = QGeoCoordinate(0, 0, 0); + _gpsRefSet = false; + emit gpsRefChanged(_gpsRefPoint, _gpsRefSet); +} + +void OsmParser::setBuildingLevelHeight(QVariant value) +{ + _buildingLevelHeight = value.toFloat(); + emit buildingLevelHeightChanged(); } void OsmParser::parseOsmFile(QString filePath) { + if(filePath == "Please select an OSM file"){ + if(_mapLoadedFlag){ + qDebug("The 3D View has been cleared!"); + _mapNodes.clear(); + _mapBuildings.clear(); + _gpsRefSet = false; + _mapLoadedFlag = false; + resetGpsRef(); + }else{ + qDebug("No OSM File is selected!"); + } + return; + } + //The QDomDocument class represents an XML document. QDomDocument xml_content; // Load xml file as raw data @@ -57,11 +87,6 @@ void OsmParser::parseOsmFile(QString filePath) QDomElement component = root.firstChild().toElement(); - _mapNodes.clear(); - _mapBuildings.clear(); - _gpsRefSet = false; - _mapLoadedFlag = false; - while(!component.isNull()) { decodeNodeTags(component, _mapNodes); decodeBuildings(component, _mapBuildings, _mapNodes, _gpsRefPoint); @@ -69,8 +94,8 @@ void OsmParser::parseOsmFile(QString filePath) component = component.nextSibling().toElement(); } _mapLoadedFlag = true; - emit newMapLoaded(); - qDebug() << _mapBuildings.size() << " Buildings added to the 3D viewer!!!"; + emit mapChanged(); + qDebug() << _mapBuildings.size() << " Buildings loaded!!!"; } void OsmParser::decodeNodeTags(QDomElement &xmlComponent, QMap &nodeMap) @@ -151,8 +176,8 @@ void OsmParser::decodeBuildings(QDomElement &xmlComponent, QMap 2 && (bld_tmp.height > 0 || bld_tmp.levels > 0)) { -// float bld_height = (bld_tmp.height >= bld_tmp.levels * _buildingLevelHeight)?(bld_tmp.height):(bld_tmp.levels * _buildingLevelHeight); -// bld_tmp.height = bld_height; + // float bld_height = (bld_tmp.height >= bld_tmp.levels * _buildingLevelHeight)?(bld_tmp.height):(bld_tmp.levels * _buildingLevelHeight); + // bld_tmp.height = bld_height; bld_tmp.points_gps = bld_points; bld_tmp.points_local = bld_points_local; bld_tmp.bb_max = QVector2D(bld_x_max, bld_y_max); @@ -172,7 +197,7 @@ QByteArray OsmParser::buildingToMesh() std::vector > > polygon; std::vector triangulated_mesh; -// bld_height = (ii.value().height >= ii.value().levels * _buildingLevelHeight)?(ii.value().height):(ii.value().levels * _buildingLevelHeight); + // bld_height = (ii.value().height >= ii.value().levels * _buildingLevelHeight)?(ii.value().height):(ii.value().levels * _buildingLevelHeight); if(ii.value().height > 0){ bld_height = ii.value().height; diff --git a/src/Viewer3D/OsmParser.h b/src/Viewer3D/OsmParser.h index 77ee0eba26d..07f847c091e 100644 --- a/src/Viewer3D/OsmParser.h +++ b/src/Viewer3D/OsmParser.h @@ -9,9 +9,12 @@ #include #include #include "qgeocoordinate.h" +#include /// @author Omid Esrafilian +class Viewer3DSettings; + class OsmParser : public QObject { struct BuildingType @@ -26,14 +29,15 @@ class OsmParser : public QObject Q_OBJECT - Q_PROPERTY(float buildingLevelHeight READ buildingLevelHeight WRITE setBuildingLevelHeight NOTIFY buildingLevelHeightChanged) + // Q_PROPERTY(float buildingLevelHeight READ buildingLevelHeight WRITE setBuildingLevelHeight NOTIFY buildingLevelHeightChanged) public: explicit OsmParser(QObject *parent = nullptr); void setGpsRef(QGeoCoordinate gpsRef); + void resetGpsRef(); QGeoCoordinate getGpsRef(){ return _gpsRefPoint;} - void setBuildingLevelHeight(float levelHeight){_buildingLevelHeight = levelHeight; emit buildingLevelHeightChanged();} + float buildingLevelHeight(void){return _buildingLevelHeight;} void parseOsmFile(QString filePath); void decodeNodeTags(QDomElement& xmlComponent, QMap &nodeMap); @@ -45,7 +49,6 @@ class OsmParser : public QObject void trianglateRectangle(std::vector& triangulatedMesh, std::vector verticesCcw, bool invertNormal); private: - QThread* _mainThread; QGeoCoordinate _gpsRefPoint; QMap _mapNodes; QMap _mapBuildings; @@ -53,13 +56,17 @@ class OsmParser : public QObject bool _gpsRefSet; float _buildingLevelHeight; bool _mapLoadedFlag; + Viewer3DSettings* _viewer3DSettings = nullptr; signals: - void gpsRefChanged(QGeoCoordinate newGpsRef); - void newMapLoaded(); + void gpsRefChanged(QGeoCoordinate newGpsRef, bool isRefSet); + void mapChanged(); void buildingLevelHeightChanged(void); +private slots: + void setBuildingLevelHeight(QVariant value); + }; #endif // OSMPARSER_H diff --git a/src/Viewer3D/Viewer3D/Models3D/CameraLightModel.qml b/src/Viewer3D/Viewer3D/Models3D/CameraLightModel.qml index e0cc6b8bd65..2bd1f64bbf4 100644 --- a/src/Viewer3D/Viewer3D/Models3D/CameraLightModel.qml +++ b/src/Viewer3D/Viewer3D/Models3D/CameraLightModel.qml @@ -11,7 +11,23 @@ Node { property real _tilt: 0.001 property real _pan: 0.001 - property real _zoom: 1000 + property real _zoom: 1500 + + function resetCamera(){ + camNode.position = Qt.vector3d(0, 0, 0); + camNode.eulerRotation = Qt.vector3d(90, 0, 0); + + cameraPerspectiveThree.position = Qt.vector3d(0, 0, 0); + cameraPerspectiveThree.eulerRotation = Qt.vector3d(0, 0, 0); + + cameraPerspectiveTwo.position = Qt.vector3d(_zoom * Math.sin(_tilt) * Math.cos(_pan), + _zoom * Math.cos(_tilt), + _zoom * Math.sin(_tilt) * Math.sin(_pan)); + cameraPerspectiveTwo.eulerRotation = Qt.vector3d(0, 0, 0); + + cameraPerspectiveOne.position = Qt.vector3d(0, 0, 0); + cameraPerspectiveOne.eulerRotation = Qt.vector3d(-90, 0, 0); + } DirectionalLight { @@ -57,6 +73,11 @@ Node { PerspectiveCamera { id: cameraPerspectiveOne + clipFar: 100000 + + eulerRotation{ + x: -90 + } } } diff --git a/src/Viewer3D/Viewer3D/Models3D/Viewer3DModel.qml b/src/Viewer3D/Viewer3D/Models3D/Viewer3DModel.qml index a989329d325..b0b02f37327 100644 --- a/src/Viewer3D/Viewer3D/Models3D/Viewer3DModel.qml +++ b/src/Viewer3D/Viewer3D/Models3D/Viewer3DModel.qml @@ -21,7 +21,59 @@ import QGroundControl.Vehicle View3D { id: topView property var viewer3DManager: null + readonly property var _gpsRef: viewer3DManager.qmlBackend.gpsRef + property bool isViewer3DOpen: false + property real rotationSpeed: 0.1 + property real movementSpeed: 1 + property real zoomSpeed: 0.3 + function rotateCamera(newPose: vector2d, lastPose: vector2d) { + let rotation_vec = Qt.vector2d(newPose.y - lastPose.y, newPose.x - lastPose.x); + + let dx_l = rotation_vec.x * rotationSpeed + let dy_l = rotation_vec.y * rotationSpeed + + standAloneScene.cameraOneRotation.x += dx_l + standAloneScene.cameraOneRotation.y += dy_l + } + + function moveCamera(newPose: vector2d, lastPose: vector2d) { + let _roll = standAloneScene.cameraOneRotation.x * (3.1415/180) + let _pitch = standAloneScene.cameraOneRotation.y * (3.1415/180) + + let dx_l = (newPose.x - lastPose.x) * movementSpeed + let dy_l = (newPose.y - lastPose.y) * movementSpeed + + //Note: Rotation Matrix is computed as: R = R(-_pitch) * R(_roll) + // Then the corerxt tramslation is: d = R * [dx_l; dy_l; dz_l] + + let dx = dx_l * Math.cos(_pitch) - dy_l * Math.sin(_pitch) * Math.sin(_roll) + let dy = dy_l * Math.cos(_roll) + let dz = dx_l * Math.sin(_pitch) + dy_l * Math.cos(_pitch) * Math.sin(_roll) + + standAloneScene.cameraTwoPosition.x -= dx + standAloneScene.cameraTwoPosition.y += dy + standAloneScene.cameraTwoPosition.z += dz + } + + function zoomCamera(zoomValue){ + let dz_l = zoomValue * zoomSpeed; + + let _roll = standAloneScene.cameraOneRotation.x * (3.1415/180) + let _pitch = standAloneScene.cameraOneRotation.y * (3.1415/180) + + let dx = -dz_l * Math.cos(_roll) * Math.sin(_pitch) + let dy = -dz_l * Math.sin(_roll) + let dz = dz_l * Math.cos(_pitch) * Math.cos(_roll) + + standAloneScene.cameraTwoPosition.x -= dx + standAloneScene.cameraTwoPosition.y += dy + standAloneScene.cameraTwoPosition.z += dz + } + + on_GpsRefChanged:{ + standAloneScene.resetCamera(); + } camera: standAloneScene.cameraOne importScene: CameraLightModel{ @@ -31,7 +83,7 @@ View3D { // renderMode: View3D.Inline environment: SceneEnvironment { - clearColor: "white" + clearColor: "#F9F9F9" backgroundMode: SceneEnvironment.Color } @@ -42,7 +94,6 @@ View3D { geometry: CityMapGeometry { id: cityMapGeometry modelName: "city_map" - osmFilePath: (viewer3DManager)?(viewer3DManager.qmlBackend.osmFilePath):("nan") osmParser: (viewer3DManager)?(viewer3DManager.osmParser):(null) } @@ -78,66 +129,102 @@ View3D { vehicle3DLoader.sourceComponent = vehicle3DComponent } - MouseArea{ - property real _transferSpeed: 1 - property real _rotationSpeed: 0.1 - property real _zoomSpeed: 0.3 + DragHandler { + property bool _isMoving: false property point _lastPose; - anchors.fill: parent - acceptedButtons: Qt.AllButtons; - - - onPressed: (mouse)=> { - _lastPose = Qt.point(mouse.x, mouse.y); - } - - onPositionChanged: (mouse)=> { - let _roll = standAloneScene.cameraOneRotation.x * (3.1415/180) - let _pitch = standAloneScene.cameraOneRotation.y * (3.1415/180) - // let yaw = standAloneScene.cameraOneRotation.z * (3.1415/180) + id: cameraMovementDragHandler + target: null + acceptedModifiers: Qt.NoModifier + acceptedButtons: Qt.LeftButton + enabled: isViewer3DOpen - if (mouse.buttons === Qt.LeftButton) { // Left button for translate - let dx_l = (mouse.x - _lastPose.x) * _transferSpeed - let dy_l = (mouse.y - _lastPose.y) * _transferSpeed - - //Note: Rotation Matrix is computed as: R = R(-_pitch) * R(_roll) - // Then the corerxt tramslation is: d = R * [dx_l; dy_l; dz_l] + onCentroidChanged: { + if(_isMoving){ + moveCamera(centroid.position, _lastPose); + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + } + } - let dx = dx_l * Math.cos(_pitch) - dy_l * Math.sin(_pitch) * Math.sin(_roll) - let dy = dy_l * Math.cos(_roll) - let dz = dx_l * Math.sin(_pitch) + dy_l * Math.cos(_pitch) * Math.sin(_roll) + onActiveChanged: { + if(active){ // When mouse is pressed + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + _isMoving = true + }else{ // When mouse is released + _isMoving = false + } + } + } - standAloneScene.cameraTwoPosition.x -= dx - standAloneScene.cameraTwoPosition.y += dy - standAloneScene.cameraTwoPosition.z += dz - }else if (mouse.buttons === Qt.RightButton){ // Right button for rotation + DragHandler { + property bool _isRotating: false + property point _lastPose; - let rotation_vec = Qt.vector2d(mouse.y - _lastPose.y, mouse.x - _lastPose.x); + id: cameraRotationDragHandler + target: null + acceptedModifiers: Qt.NoModifier + acceptedButtons: Qt.RightButton + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + enabled: isViewer3DOpen + + onCentroidChanged: { + if(_isRotating){ + rotateCamera(centroid.position, _lastPose); + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + } + } - let dx_l = rotation_vec.x * _rotationSpeed - let dy_l = rotation_vec.y * _rotationSpeed + onActiveChanged: { + if(active){ // When mouse is pressed + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + _isRotating = true + }else{// When mouse is released + _isRotating = false + } + } + } - standAloneScene.cameraOneRotation.x += dx_l - standAloneScene.cameraOneRotation.y += dy_l + PinchHandler { + id: zoomRotationPinchHandler + target: null - } - _lastPose = Qt.point(mouse.x, mouse.y) - } + property bool _isRotating: false + property point _lastPose; + property real _lastZoomValue; + enabled: isViewer3DOpen - onWheel: (wheel)=> { - let dz_l = -wheel.angleDelta.y * _zoomSpeed + onCentroidChanged: { + if(_isRotating){ + rotateCamera(centroid.position, _lastPose); + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + } + } - let _roll = standAloneScene.cameraOneRotation.x * (3.1415/180) - let _pitch = standAloneScene.cameraOneRotation.y * (3.1415/180) + onActiveChanged: { + if (active) { + _lastPose = Qt.point(centroid.position.x, centroid.position.y); + _lastZoomValue = 0 + _isRotating = true; + } else { + _isRotating = false; + } + } + onActiveScaleChanged: { + let zoomValue = (activeScale > 1)?(activeScale - 1):(-((1/activeScale) - 1)) + zoomCamera(- 1000 * (zoomValue - _lastZoomValue)) + _lastZoomValue = zoomValue + } + } - let dx = -dz_l * Math.cos(_roll) * Math.sin(_pitch) - let dy = -dz_l * Math.sin(_roll) - let dz = dz_l * Math.cos(_pitch) * Math.cos(_roll) + WheelHandler { + id: wheelHandler + orientation: Qt.Vertical + target: null + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + enabled: isViewer3DOpen - standAloneScene.cameraTwoPosition.x -= dx - standAloneScene.cameraTwoPosition.y += dy - standAloneScene.cameraTwoPosition.z += dz + onWheel: event => { + zoomCamera(-event.angleDelta.y) } } } diff --git a/src/Viewer3D/Viewer3D/Models3D/Viewer3DVehicleItems.qml b/src/Viewer3D/Viewer3D/Models3D/Viewer3DVehicleItems.qml index 59f74758ad3..ca586e5115f 100644 --- a/src/Viewer3D/Viewer3D/Models3D/Viewer3DVehicleItems.qml +++ b/src/Viewer3D/Viewer3D/Models3D/Viewer3DVehicleItems.qml @@ -23,6 +23,9 @@ Node { property var _vehicle: null property var _planMasterController: null property var _missionController: (_planMasterController)?(_planMasterController.missionController):(null) + property var _viewer3DSetting: QGroundControl.settingsManager.viewer3DSettings + property var _altitudeBias: _viewer3DSetting.altitudeBias.rawValue + function addMissionItemsToListModel() { missionWaypointListModel.clear() @@ -85,7 +88,7 @@ Node { id: droneDji3DModel vehicle: _vehicle modelScale: Qt.vector3d(0.05, 0.05, 0.05) - altitudeBias: _backendQml.altitudeBias + altitudeBias: _altitudeBias gpsRef: _backendQml.gpsRef } @@ -96,7 +99,7 @@ Node { delegate: Waypoint3DModel{ opacity: 0.8 missionItem: model - altitudeBias: _backendQml.altitudeBias + altitudeBias: _altitudeBias } } @@ -105,8 +108,8 @@ Node { model: missionPathModel delegate: Line3D{ - p_1: Qt.vector3d(model.x_1 * 10, model.y_1 * 10, (model.z_1 + _backendQml.altitudeBias) * 10) - p_2: Qt.vector3d(model.x_2 * 10, model.y_2 * 10, (model.z_2 + _backendQml.altitudeBias) * 10) + p_1: Qt.vector3d(model.x_1 * 10, model.y_1 * 10, (model.z_1 + _altitudeBias) * 10) + p_2: Qt.vector3d(model.x_2 * 10, model.y_2 * 10, (model.z_2 + _altitudeBias) * 10) lineWidth:8 color: "orange" } diff --git a/src/Viewer3D/Viewer3D/Viewer3D.qml b/src/Viewer3D/Viewer3D/Viewer3D.qml index 309742dcb84..926b7795100 100644 --- a/src/Viewer3D/Viewer3D/Viewer3D.qml +++ b/src/Viewer3D/Viewer3D/Viewer3D.qml @@ -3,73 +3,75 @@ import QtQuick.Controls import QtQuick.Dialogs import QtQuick.Layouts +import QGroundControl import QGroundControl.Palette import QGroundControl.ScreenTools import QGroundControl.Controls -// 3D Viewer modules import QGroundControl.Viewer3D import Viewer3D.Models3D +/// @author Omid Esrafilian + Item{ id: viewer3DBody - property bool viewer3DOpen: false - property bool settingMenuOpen: false + property bool isOpen: false + property bool _viewer3DEnabled: QGroundControl.settingsManager.viewer3DSettings.enabled.rawValue + - Viewer3DManager{ - id: _viewer3DManager + function open(){ + if(_viewer3DEnabled === true){ + view3DManagerLoader.sourceComponent = viewer3DManagerComponent + view3DManagerLoader.active = true; + viewer3DBody.z = 1 + isOpen = true; + } } - Loader{ - id: view3DLoader - anchors.fill: parent + function close(){ + viewer3DBody.z = 0 + isOpen = false; + } - onLoaded: { - item.viewer3DManager = _viewer3DManager + on_Viewer3DEnabledChanged: { + if(_viewer3DEnabled === false){ + viewer3DBody.close(); + view3DLoader.active = false; + view3DManagerLoader.active = false; } } - onViewer3DOpenChanged: { - view3DLoader.source = "Models3D/Viewer3DModel.qml" - if(viewer3DOpen){ - viewer3DBody.z = 1 - }else{ - viewer3DBody.z = 0 + Component{ + id: viewer3DManagerComponent + + Viewer3DManager{ + id: _viewer3DManager } } - onSettingMenuOpenChanged:{ - if(settingMenuOpen === true){ - settingMenuComponent.createObject(mainWindow).open() + Loader{ + id: view3DManagerLoader + + onLoaded: { + view3DLoader.source = "Models3D/Viewer3DModel.qml" + view3DLoader.active = true; } } - Component { - id: settingMenuComponent - - QGCPopupDialog{ - id: settingMenuDialog - title: qsTr("3D view setting") - buttons: Dialog.Ok | Dialog.Cancel - - Viewer3DSettingMenu{ - id: viewer3DSettingMenu - viewer3DManager: _viewer3DManager - visible: true - } - - onRejected: { - settingMenuOpen = false - viewer3DSettingMenu.menuClosed(false) - settingMenuDialog.close() - } - - onAccepted: { - settingMenuOpen = false - viewer3DSettingMenu.menuClosed(true) - settingMenuDialog.close() - } + Loader{ + id: view3DLoader + anchors.fill: parent + + onLoaded: { + item.viewer3DManager = view3DManagerLoader.item } } + + Binding{ + target: view3DLoader.item + property: "isViewer3DOpen" + value: isOpen + when: view3DLoader.status == Loader.Ready + } } diff --git a/src/Viewer3D/Viewer3D/Viewer3DSettingMenu.qml b/src/Viewer3D/Viewer3D/Viewer3DSettingMenu.qml deleted file mode 100644 index dac15666fd8..00000000000 --- a/src/Viewer3D/Viewer3D/Viewer3DSettingMenu.qml +++ /dev/null @@ -1,171 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Dialogs -import QtQuick.Layouts - -import QGroundControl.Palette -import QGroundControl.ScreenTools -import QGroundControl.Controls - -import QGroundControl.Viewer3D - -/// @author Omid Esrafilian - -Rectangle { - - signal menuClosed(bool accept) - - property var viewer3DManager: null - property int leftMarginSpace: ScreenTools.defaultFontPixelWidth - - id: window_body - clip: true - color: qgcPal.window - visible: true - width: Screen.width * 0.25 - height: Screen.width * 0.15 - - QGCPalette { - id: qgcPal - colorGroupEnabled: enabled - } - - QGCLabel { - id: map_file_label - Layout.fillWidth: true - wrapMode: Text.WordWrap - visible: true - text: qsTr("3D Map File:") - anchors.left: parent.left - anchors.top: parent.top - anchors.leftMargin: leftMarginSpace - anchors.topMargin: ScreenTools.defaultFontPixelWidth * 3 - } - - QGCButton { - id: map_file_btn - anchors.right: map_file_text_feild.right - anchors.top: map_file_text_feild.bottom - anchors.topMargin: ScreenTools.defaultFontPixelWidth * 2 - anchors.rightMargin: ScreenTools.defaultFontPixelWidth - - visible: true - text: qsTr("Select File") - - onClicked: { - fileDialog.openForLoad() - } - - QGCFileDialog { - id: fileDialog - - nameFilters: [qsTr("OpenStreetMap files (*.osm)")] - title: qsTr("Select map file") - onAcceptedForLoad: (file) => { - map_file_text_feild.text = file - } - } - } - - QGCTextField { - id: map_file_text_feild - height: ScreenTools.defaultFontPixelWidth * 4.5 - unitsLabel: "" - showUnits: false - visible: true - - anchors.verticalCenter: map_file_label.verticalCenter - anchors.right: parent.right - anchors.left: map_file_label.right - anchors.rightMargin: 20 - anchors.leftMargin: 20 - readOnly: true - - text: (viewer3DManager)?(viewer3DManager.viewer3DSetting.osmFilePath.rawValue):("nan") - } - - QGCLabel { - id: bld_level_height - Layout.fillWidth: true - wrapMode: Text.WordWrap - visible: true - text: qsTr("Average Building Level Height:") - anchors.left: parent.left - anchors.top: map_file_btn.bottom - anchors.leftMargin: leftMarginSpace - anchors.topMargin: ScreenTools.defaultFontPixelWidth * 2 - } - - QGCTextField { - id: bld_level_height_textfeild - width: ScreenTools.defaultFontPixelWidth * 15 - unitsLabel: "m" - showUnits: true - numericValuesOnly: true - visible: true - - anchors.verticalCenter: bld_level_height.verticalCenter - anchors.left: bld_level_height.right - anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 - - text: (viewer3DManager)?(Number(viewer3DManager.viewer3DSetting.buildingLevelHeight.rawValue)):("nan") - - validator: RegularExpressionValidator{ - regularExpression: /(-?\d{1,10})([.]\d{1,6})?$/ - } - - onAccepted: - { - focus = false - } - } - - QGCLabel { - id: height_bias_label - Layout.fillWidth: true - wrapMode: Text.WordWrap - visible: true - text: qsTr("Vehicles Altitude Bias:") - anchors.left: parent.left - anchors.top: bld_level_height.bottom - anchors.leftMargin: leftMarginSpace - anchors.topMargin: ScreenTools.defaultFontPixelWidth * 4 - } - - QGCTextField { - id: height_bias_textfeild - width: ScreenTools.defaultFontPixelWidth * 15 - unitsLabel: "m" - showUnits: true - numericValuesOnly: true - visible: true - - anchors.verticalCenter: height_bias_label.verticalCenter - anchors.left: height_bias_label.right - anchors.leftMargin: ScreenTools.defaultFontPixelWidth * 2 - - text: (viewer3DManager)?(Number(viewer3DManager.viewer3DSetting.altitudeBias.rawValue)):("nan") - - validator: RegularExpressionValidator{ - regularExpression: /(-?\d{1,10})([.]\d{1,6})?$/ - } - - onAccepted: - { - focus = false - } - } - - onMenuClosed: function (accept){ - if(accept === true){ - viewer3DManager.qmlBackend.osmFilePath = map_file_text_feild.text - viewer3DManager.qmlBackend.altitudeBias = parseFloat(height_bias_textfeild.text) - - viewer3DManager.viewer3DSetting.osmFilePath.rawValue = map_file_text_feild.text - viewer3DManager.viewer3DSetting.buildingLevelHeight.rawValue = parseFloat(bld_level_height_textfeild.text) - viewer3DManager.viewer3DSetting.altitudeBias.rawValue = parseFloat(height_bias_textfeild.text) - - viewer3DManager.osmParser.buildingLevelHeight = parseFloat(bld_level_height_textfeild.text) - } - } -} diff --git a/src/Viewer3D/Viewer3D/qmldir b/src/Viewer3D/Viewer3D/qmldir index 8c326b1c8ba..15a9da02437 100644 --- a/src/Viewer3D/Viewer3D/qmldir +++ b/src/Viewer3D/Viewer3D/qmldir @@ -1,4 +1,3 @@ Module Viewer3D Viewer3D 1.0 Viewer3D.qml -Viewer3DSettingMenu 1.0 Viewer3DSettingMenu.qml diff --git a/src/Viewer3D/Viewer3DManager.cc b/src/Viewer3D/Viewer3DManager.cc index 6fc52ca6cc8..9358a3558ed 100644 --- a/src/Viewer3D/Viewer3DManager.cc +++ b/src/Viewer3D/Viewer3DManager.cc @@ -5,8 +5,12 @@ Viewer3DManager::Viewer3DManager() { _qmlBackend = new Viewer3DQmlBackend(this); _osmParser = new OsmParser(); - _viewer3DSetting = new Viewer3DSettings(); - _osmParser->setBuildingLevelHeight(_viewer3DSetting->buildingLevelHeight()->rawValue().toFloat()); // meters - _qmlBackend->init(_viewer3DSetting, _osmParser); + _qmlBackend->init(_osmParser); +} + +Viewer3DManager::~Viewer3DManager() +{ + delete _osmParser; + delete _qmlBackend; } diff --git a/src/Viewer3D/Viewer3DManager.h b/src/Viewer3D/Viewer3DManager.h index ab87f0c1a12..e212c4f88ff 100644 --- a/src/Viewer3D/Viewer3DManager.h +++ b/src/Viewer3D/Viewer3DManager.h @@ -5,10 +5,11 @@ #include "Viewer3DQmlBackend.h" #include "OsmParser.h" -#include "Viewer3DSettings.h" /// @author Omid Esrafilian +class SettingsManager; + // This class contains all the variables shared between the C++ and QML sides for 3D viewer. class Viewer3DManager : public QObject { @@ -16,15 +17,15 @@ class Viewer3DManager : public QObject Q_PROPERTY(OsmParser* osmParser MEMBER _osmParser CONSTANT) Q_PROPERTY(Viewer3DQmlBackend* qmlBackend MEMBER _qmlBackend CONSTANT) - Q_PROPERTY(Viewer3DSettings* viewer3DSetting MEMBER _viewer3DSetting CONSTANT) public: explicit Viewer3DManager(); + ~Viewer3DManager(); + protected: OsmParser *_osmParser = nullptr; Viewer3DQmlBackend *_qmlBackend = nullptr; - Viewer3DSettings* _viewer3DSetting = nullptr; }; diff --git a/src/Viewer3D/Viewer3DQmlBackend.cc b/src/Viewer3D/Viewer3DQmlBackend.cc index b3186cf0404..947262f0ae2 100644 --- a/src/Viewer3D/Viewer3DQmlBackend.cc +++ b/src/Viewer3D/Viewer3DQmlBackend.cc @@ -3,35 +3,69 @@ #include #include +#include "QGCApplication.h" +#include "SettingsManager.h" + +#define GPS_REF_NOT_SET 0 +#define GPS_REF_SET_BY_MAP 1 +#define GPS_REF_SET_BY_VEHICLE 2 + Viewer3DQmlBackend::Viewer3DQmlBackend(QObject *parent) : QObject{parent} { + _gpsRefSet = GPS_REF_NOT_SET; + _activeVehicle = nullptr; + _viewer3DSettings = qgcApp()->toolbox()->settingsManager()->viewer3DSettings(); } -void Viewer3DQmlBackend::init(Viewer3DSettings *viewerSettingThr, OsmParser* osmThr) +void Viewer3DQmlBackend::init(OsmParser* osmThr) { - _altitudeBias = viewerSettingThr->altitudeBias()->rawValue().toFloat(); - _osmFilePath = viewerSettingThr->osmFilePath()->rawValue().toString(); - _osmParserThread = osmThr; + _activeVehicleChangedEvent(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()); + connect(_osmParserThread, &OsmParser::gpsRefChanged, this, &Viewer3DQmlBackend::_gpsRefChangedEvent); + connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &Viewer3DQmlBackend::_activeVehicleChangedEvent); } -void Viewer3DQmlBackend::setGpsRef(const QGeoCoordinate &gpsRef) +void Viewer3DQmlBackend::_activeVehicleChangedEvent(Vehicle *vehicle) { - if(_gpsRef == gpsRef){ - return; + if(_activeVehicle){ + disconnect(_activeVehicle, &Vehicle::coordinateChanged, this, &Viewer3DQmlBackend::_activeVehicleCoordinateChanged); } - _gpsRef = gpsRef; - emit gpsRefChanged(); + _activeVehicle = vehicle; + if(!_activeVehicle){ // means that all the vehicle have been disconnected + if(_gpsRefSet == GPS_REF_SET_BY_VEHICLE){ + _gpsRefSet = GPS_REF_NOT_SET; + } + }else{ + connect(_activeVehicle, &Vehicle::coordinateChanged, this, &Viewer3DQmlBackend::_activeVehicleCoordinateChanged); + } } -void Viewer3DQmlBackend::_gpsRefChangedEvent(QGeoCoordinate newGpsRef) -{ - _gpsRef = newGpsRef; +void Viewer3DQmlBackend::_activeVehicleCoordinateChanged(QGeoCoordinate newCoordinate) +{ + if(_gpsRefSet == GPS_REF_NOT_SET){ + if(newCoordinate.latitude() && newCoordinate.longitude()){ + _gpsRef = newCoordinate; + _gpsRef.setAltitude(0); + _gpsRefSet = GPS_REF_SET_BY_VEHICLE; + emit gpsRefChanged(); - emit gpsRefChanged(); + qDebug() << "3D viewer gps reference set by vehicles:" << _gpsRef.latitude() << _gpsRef.longitude() << _gpsRef.altitude(); + } + } +} + +void Viewer3DQmlBackend::_gpsRefChangedEvent(QGeoCoordinate newGpsRef, bool isRefSet) +{ + if(isRefSet){ + _gpsRef = newGpsRef; + _gpsRefSet = GPS_REF_SET_BY_MAP; + emit gpsRefChanged(); + }else{ + _gpsRefSet = GPS_REF_NOT_SET; + } - qDebug() << "3D map gps reference:" << _gpsRef.latitude() << _gpsRef.longitude() << _gpsRef.altitude(); + qDebug() << "3D viewer gps reference set by osm map:" << _gpsRef.latitude() << _gpsRef.longitude() << _gpsRef.altitude(); } diff --git a/src/Viewer3D/Viewer3DQmlBackend.h b/src/Viewer3D/Viewer3DQmlBackend.h index b248ad83dae..f79fbe8226b 100644 --- a/src/Viewer3D/Viewer3DQmlBackend.h +++ b/src/Viewer3D/Viewer3DQmlBackend.h @@ -10,36 +10,38 @@ /// @author Omid Esrafilian +class Viewer3DSettings; + class Viewer3DQmlBackend : public QObject { Q_OBJECT - Q_PROPERTY(QString osmFilePath MEMBER _osmFilePath NOTIFY cityMapPathChanged) Q_PROPERTY(QGeoCoordinate gpsRef READ gpsRef NOTIFY gpsRefChanged) - Q_PROPERTY(float altitudeBias MEMBER _altitudeBias NOTIFY altitudeBiasChanged) public: explicit Viewer3DQmlBackend(QObject *parent = nullptr); - void init(Viewer3DSettings* viewerSettingThr, OsmParser* osmThr=nullptr); + void init(OsmParser* osmThr=nullptr); QGeoCoordinate gpsRef(){return _gpsRef;} - void setGpsRef(const QGeoCoordinate& gpsRef); signals: void gpsRefChanged(); - void altitudeBiasChanged(); - void cityMapPathChanged(); private: OsmParser *_osmParserThread; - QString _osmFilePath; QGeoCoordinate _gpsRef; - float _altitudeBias; + uint8_t _gpsRefSet; + + Vehicle *_activeVehicle; + Viewer3DSettings* _viewer3DSettings = nullptr; + protected slots: - void _gpsRefChangedEvent(QGeoCoordinate newGpsRef); + void _gpsRefChangedEvent(QGeoCoordinate newGpsRef, bool isRefSet); + void _activeVehicleChangedEvent(Vehicle* vehicle); + void _activeVehicleCoordinateChanged(QGeoCoordinate newCoordinate); }; #endif // Viewer3DQmlBackend_H diff --git a/src/ui/preferences/FlyViewSettings.qml b/src/ui/preferences/FlyViewSettings.qml index b9fddbb1f90..29515b31a6e 100644 --- a/src/ui/preferences/FlyViewSettings.qml +++ b/src/ui/preferences/FlyViewSettings.qml @@ -30,6 +30,11 @@ SettingsPage { property Fact _guidedMinimumAltitude: _settingsManager.flyViewSettings.guidedMinimumAltitude property Fact _guidedMaximumAltitude: _settingsManager.flyViewSettings.guidedMaximumAltitude property Fact _maxGoToLocationDistance: _settingsManager.flyViewSettings.maxGoToLocationDistance + property Fact _viewer3DEnabled: _settingsManager.viewer3DSettings.enabled + property Fact _viewer3DOsmFilePath: _settingsManager.viewer3DSettings.osmFilePath + property Fact _viewer3DBuildingLevelHeight: _settingsManager.viewer3DSettings.buildingLevelHeight + property Fact _viewer3DAltitudeBias: _settingsManager.viewer3DSettings.altitudeBias + SettingsGroupLayout { Layout.fillWidth: true @@ -116,7 +121,7 @@ SettingsPage { SettingsGroupLayout { Layout.fillWidth: true - heading: qsTr("Virtual Joystick") + heading: qsTr("Virtual Joystick") visible: _virtualJoystick.visible || _virtualJoystickAutoCenterThrottle.visible FactCheckBoxSlider { @@ -154,4 +159,87 @@ SettingsPage { fact: _lockNoseUpCompass } } + + SettingsGroupLayout { + Layout.fillWidth: true + heading: qsTr("3D View") + + FactCheckBoxSlider { + Layout.fillWidth: true + text: qsTr("Enabled") + fact: _viewer3DEnabled + } + ColumnLayout{ + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelWidth + enabled: _viewer3DEnabled.rawValue + + RowLayout{ + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { + wrapMode: Text.WordWrap + visible: true + text: qsTr("3D Map File:") + } + + QGCTextField { + id: osmFileTextField + height: ScreenTools.defaultFontPixelWidth * 4.5 + unitsLabel: "" + showUnits: false + visible: true + Layout.fillWidth: true + readOnly: true + text: _viewer3DOsmFilePath.rawValue + } + } + RowLayout{ + Layout.alignment: Qt.AlignRight + spacing: ScreenTools.defaultFontPixelWidth + + QGCButton { + text: qsTr("Clear") + + onClicked: { + osmFileTextField.text = "Please select an OSM file" + _viewer3DOsmFilePath.value = osmFileTextField.text + } + } + + QGCButton { + text: qsTr("Select File") + + onClicked: { + fileDialog.openForLoad() + } + + QGCFileDialog { + id: fileDialog + nameFilters: [qsTr("OpenStreetMap files (*.osm)")] + title: qsTr("Select map file") + onAcceptedForLoad: (file) => { + osmFileTextField.text = file + _viewer3DOsmFilePath.value = osmFileTextField.text + } + } + } + } + } + + LabelledFactTextField { + Layout.fillWidth: true + label: qsTr("Average Building Level Height") + fact: _viewer3DBuildingLevelHeight + enabled: _viewer3DEnabled.rawValue + } + + LabelledFactTextField { + Layout.fillWidth: true + label: qsTr("Vehicles Altitude Bias") + fact: _viewer3DAltitudeBias + enabled: _viewer3DEnabled.rawValue + } + } }