- Change extent to trapezoid shape

- Visualize the viewed area using QgsRubberBand
-  Address reviews
This commit is contained in:
NEDJIMAbelgacem 2022-03-08 09:30:20 +01:00 committed by Martin Dobias
parent e38a879378
commit 9141d05d59
14 changed files with 226 additions and 91 deletions

View File

@ -27,13 +27,6 @@ Definition of the world.
#include "qgs3dmapsettings.h" #include "qgs3dmapsettings.h"
%End %End
public: public:
enum ViewSyncMode
{
NoSync,
Sync3DTo2D,
Sync2DTo3D
};
Qgs3DMapSettings(); Qgs3DMapSettings();
%Docstring %Docstring
Constructor for Qgs3DMapSettings Constructor for Qgs3DMapSettings
@ -628,17 +621,31 @@ Sets the renderer usage
.. versionadded:: 3.24 .. versionadded:: 3.24
%End %End
ViewSyncMode viewSyncMode() const; Qgis::ViewSyncMode viewSyncMode() const;
%Docstring %Docstring
Returns the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation) Returns the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation)
.. versionadded:: 3.26 .. versionadded:: 3.26
%End %End
void setViewSyncMode( ViewSyncMode mode ); void setViewSyncMode( Qgis::ViewSyncMode mode );
%Docstring %Docstring
Sets the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation) Sets the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation)
.. versionadded:: 3.26
%End
bool viewFrustumVisualizationEnabled() const;
%Docstring
Returns whether the camera's view frustum is visualized on the 2D map canvas
.. versionadded:: 3.26
%End
void setViewFrustumVisualizationEnabled( bool enabled );
%Docstring
Sets whether the camera's view frustum is visualized on the 2D map canvas
.. versionadded:: 3.26 .. versionadded:: 3.26
%End %End
@ -849,6 +856,13 @@ Emitted when shadow rendering settings are changed
Emitted when the FPS counter is enabled or disabled Emitted when the FPS counter is enabled or disabled
.. versionadded:: 3.18 .. versionadded:: 3.18
%End
void viewFrustumVisualizationEnabledChanged();
%Docstring
Emitted when the camera's view frustum visualization on the main 2D map canvas is enabled or disabled
.. versionadded:: 3.26
%End %End
private: private:

View File

@ -1303,6 +1303,14 @@ Qgis.RendererUsage.__doc__ = 'Usage of the renderer.\n\n.. versionadded:: 3.24\n
# -- # --
Qgis.RendererUsage.baseClass = Qgis Qgis.RendererUsage.baseClass = Qgis
# monkey patching scoped based enum # monkey patching scoped based enum
Qgis.ViewSyncMode.NoSync.__doc__ = ""
Qgis.ViewSyncMode.Sync3DTo2D.__doc__ = ""
Qgis.ViewSyncMode.Sync2DTo3D.__doc__ = ""
Qgis.ViewSyncMode.BothWaysSync.__doc__ = ""
Qgis.ViewSyncMode.__doc__ = '\n\n' + '* ``NoSync``: ' + Qgis.ViewSyncMode.NoSync.__doc__ + '\n' + '* ``Sync3DTo2D``: ' + Qgis.ViewSyncMode.Sync3DTo2D.__doc__ + '\n' + '* ``Sync2DTo3D``: ' + Qgis.ViewSyncMode.Sync2DTo3D.__doc__ + '\n' + '* ``BothWaysSync``: ' + Qgis.ViewSyncMode.BothWaysSync.__doc__
# --
Qgis.ViewSyncMode.baseClass = Qgis
# monkey patching scoped based enum
Qgis.HistoryProviderBackend.LocalProfile.__doc__ = "Local profile" Qgis.HistoryProviderBackend.LocalProfile.__doc__ = "Local profile"
Qgis.HistoryProviderBackend.__doc__ = 'History provider backends.\n\n.. versionadded:: 3.24\n\n' + '* ``LocalProfile``: ' + Qgis.HistoryProviderBackend.LocalProfile.__doc__ Qgis.HistoryProviderBackend.__doc__ = 'History provider backends.\n\n.. versionadded:: 3.24\n\n' + '* ``LocalProfile``: ' + Qgis.HistoryProviderBackend.LocalProfile.__doc__
# -- # --

View File

@ -835,6 +835,14 @@ The development version
Unknown, Unknown,
}; };
enum class ViewSyncMode
{
NoSync,
Sync3DTo2D,
Sync2DTo3D,
BothWaysSync
};
enum class HistoryProviderBackend enum class HistoryProviderBackend
{ {
LocalProfile, LocalProfile,

View File

@ -146,6 +146,7 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
connect( &map, &Qgs3DMapSettings::fpsCounterEnabledChanged, this, &Qgs3DMapScene::fpsCounterEnabledChanged ); connect( &map, &Qgs3DMapSettings::fpsCounterEnabledChanged, this, &Qgs3DMapScene::fpsCounterEnabledChanged );
connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged ); connect( &map, &Qgs3DMapSettings::cameraMovementSpeedChanged, this, &Qgs3DMapScene::onCameraMovementSpeedChanged );
connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url ) connect( QgsApplication::sourceCache(), &QgsSourceCache::remoteSourceFetched, this, [ = ]( const QString & url )
{ {
const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers; const QList<QgsMapLayer *> modelVectorLayers = mModelVectorLayers;
@ -247,32 +248,56 @@ void Qgs3DMapScene::viewZoomFull()
mCameraController->resetView( 1.5 * std::sqrt( a * a - side * side ) ); // assuming FOV being 45 degrees mCameraController->resetView( 1.5 * std::sqrt( a * a - side * side ) ); // assuming FOV being 45 degrees
} }
void Qgs3DMapScene::viewExtent( const QgsRectangle &extent ) void Qgs3DMapScene::setViewFrom2DExtent( const QgsRectangle &extent )
{ {
QgsPointXY center = extent.center(); QgsPointXY center = extent.center();
QgsVector3D centerWrld = mMap.mapToWorldCoordinates( QVector3D( center.x(), center.y(), 0 ) ); QgsVector3D centerWorld = mMap.mapToWorldCoordinates( QVector3D( center.x(), center.y(), 0 ) );
QgsVector3D p1 = mMap.mapToWorldCoordinates( QVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) ); QgsVector3D p1 = mMap.mapToWorldCoordinates( QVector3D( extent.xMinimum(), extent.yMinimum(), 0 ) );
QgsVector3D p2 = mMap.mapToWorldCoordinates( QVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) ); QgsVector3D p2 = mMap.mapToWorldCoordinates( QVector3D( extent.xMaximum(), extent.yMaximum(), 0 ) );
float side = std::max( std::abs( p1.x() - p2.x() ), std::abs( p1.z() - p2.z() ) ); float xSide = std::abs( p1.x() - p2.x() );
float a = side / 2.0f / std::sin( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2.0f ); float ySide = std::abs( p1.z() - p2.z() );
mCameraController->setViewFromTop( centerWrld.x(), centerWrld.z(), std::sqrt( a * a - side * side ) ); if ( xSide < ySide )
{
float fov = 2 * std::atan( std::tan( qDegreesToRadians( cameraController()->camera()->fieldOfView() ) / 2 ) * cameraController()->camera()->aspectRatio() );
float r = xSide / 2.0f / std::tan( fov / 2.0f );
mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
}
else
{
float fov = qDegreesToRadians( cameraController()->camera()->fieldOfView() );
float r = ySide / 2.0f / std::tan( fov / 2.0f );
mCameraController->setViewFromTop( centerWorld.x(), centerWorld.z(), r );
}
} }
QgsRectangle Qgs3DMapScene::viewFrustum2DExtent() QVector<QgsPointXY> Qgs3DMapScene::viewFrustum2DExtent()
{ {
QVector4D p = mCameraController->camera()->projectionMatrix() * mCameraController->camera()->viewMatrix() * mCameraController->camera()->viewCenter(); Qt3DRender::QCamera *camera = mCameraController->camera();
double maxDepth = p.z();// p.w();
const QRect viewport = mCameraController->viewport(); const QRect viewport = mCameraController->viewport();
QgsRectangle extent; QVector<QgsPointXY> extent;
extent.setMinimal(); QVector<int> pointsOrder = { 0, 1, 3, 2 };
for ( int i = 0; i < 8; ++i ) for ( int i : pointsOrder )
{ {
const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : viewport.width(), ( ( i >> 1 ) & 1 ) ? 0 : viewport.height() ); const QPoint p( ( ( i >> 0 ) & 1 ) ? 0 : viewport.width(), ( ( i >> 1 ) & 1 ) ? 0 : viewport.height() );
const double depth = ( ( i >> 2 ) & 1 ) ? 0 : maxDepth; QgsRay3D ray = Qgs3DUtils::rayFromScreenPoint( p, viewport.size(), camera );
QVector3D pWorld = Qgs3DUtils::screenPointToWorldPos( p, depth, viewport.size(), mCameraController->camera() ); QVector3D dir = ray.direction();
QgsVector3D pMap = mMap.worldToMapCoordinates( pWorld ); if ( dir.y() == 0.0 )
extent.include( QgsPointXY( pMap.x(), pMap.y() ) ); dir.setY( 0.0001 );
double t = - ray.origin().y() / dir.y();
if ( t < 0 )
{
// If the projected point is on the back of the camera we choose the farthest point in the front
t = camera->farPlane();
}
else
{
// If the projected point is on the front of the camera we choose the closest between it and farthest point in the front
t = std::min<float>( t, camera->farPlane() );
}
QVector3D planePoint = ray.origin() + t * dir;
QgsVector3D pMap = mMap.worldToMapCoordinates( planePoint );
extent.push_back( QgsPointXY( pMap.x(), pMap.y() ) );
} }
return extent; return extent;
} }
@ -385,7 +410,7 @@ void Qgs3DMapScene::onCameraChanged()
onShadowSettingsChanged(); onShadowSettingsChanged();
QgsRectangle extent2D = viewFrustum2DExtent(); QVector<QgsPointXY> extent2D = viewFrustum2DExtent();
emit viewed2DExtentFrom3DChanged( extent2D ); emit viewed2DExtentFrom3DChanged( extent2D );
} }

View File

@ -86,14 +86,14 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
void viewExtent( const QgsRectangle &extent ); void setViewFrom2DExtent( const QgsRectangle &extent );
/** /**
* Calculates the 2D extent viewed by the 3D camera * Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
QgsRectangle viewFrustum2DExtent(); QVector<QgsPointXY> viewFrustum2DExtent();
//! Returns number of pending jobs of the terrain entity //! Returns number of pending jobs of the terrain entity
int terrainPendingJobsCount() const; int terrainPendingJobsCount() const;
@ -165,7 +165,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
void viewed2DExtentFrom3DChanged( QgsRectangle extent ); void viewed2DExtentFrom3DChanged( QVector<QgsPointXY> extent );
public slots: public slots:
//! Updates the temporale entities //! Updates the temporale entities

View File

@ -84,6 +84,7 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
, mEyeDomeLightingStrength( other.mEyeDomeLightingStrength ) , mEyeDomeLightingStrength( other.mEyeDomeLightingStrength )
, mEyeDomeLightingDistance( other.mEyeDomeLightingDistance ) , mEyeDomeLightingDistance( other.mEyeDomeLightingDistance )
, mViewSyncMode( other.mViewSyncMode ) , mViewSyncMode( other.mViewSyncMode )
, mVisualizeViewFrustum( other.mVisualizeViewFrustum )
, mDebugShadowMapEnabled( other.mDebugShadowMapEnabled ) , mDebugShadowMapEnabled( other.mDebugShadowMapEnabled )
, mDebugShadowMapCorner( other.mDebugShadowMapCorner ) , mDebugShadowMapCorner( other.mDebugShadowMapCorner )
, mDebugShadowMapSize( other.mDebugShadowMapSize ) , mDebugShadowMapSize( other.mDebugShadowMapSize )
@ -273,7 +274,8 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
mEyeDomeLightingDistance = elemEyeDomeLighting.attribute( "eye-dome-lighting-distance", QStringLiteral( "1" ) ).toInt(); mEyeDomeLightingDistance = elemEyeDomeLighting.attribute( "eye-dome-lighting-distance", QStringLiteral( "1" ) ).toInt();
QDomElement elemNavigationSync = elem.firstChildElement( QStringLiteral( "navigation-sync" ) ); QDomElement elemNavigationSync = elem.firstChildElement( QStringLiteral( "navigation-sync" ) );
mViewSyncMode = ( Qgs3DMapSettings::ViewSyncMode )( elemNavigationSync.attribute( QStringLiteral( "view-sync-mode" ), QStringLiteral( "0" ) ).toInt() ); mViewSyncMode = ( Qgis::ViewSyncMode )( elemNavigationSync.attribute( QStringLiteral( "view-sync-mode" ), QStringLiteral( "0" ) ).toInt() );
mVisualizeViewFrustum = elemNavigationSync.attribute( QStringLiteral( "view-frustum-visualization-enabled" ), QStringLiteral( "0" ) ).toInt();
QDomElement elemDebugSettings = elem.firstChildElement( QStringLiteral( "debug-settings" ) ); QDomElement elemDebugSettings = elem.firstChildElement( QStringLiteral( "debug-settings" ) );
mDebugShadowMapEnabled = elemDebugSettings.attribute( QStringLiteral( "shadowmap-enabled" ), QStringLiteral( "0" ) ).toInt(); mDebugShadowMapEnabled = elemDebugSettings.attribute( QStringLiteral( "shadowmap-enabled" ), QStringLiteral( "0" ) ).toInt();
@ -414,6 +416,7 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
QDomElement elemNavigationSync = doc.createElement( QStringLiteral( "navigation-sync" ) ); QDomElement elemNavigationSync = doc.createElement( QStringLiteral( "navigation-sync" ) );
elemNavigationSync.setAttribute( QStringLiteral( "view-sync-mode" ), ( int )mViewSyncMode ); elemNavigationSync.setAttribute( QStringLiteral( "view-sync-mode" ), ( int )mViewSyncMode );
elemNavigationSync.setAttribute( QStringLiteral( "view-frustum-visualization-enabled" ), mVisualizeViewFrustum ? QStringLiteral( "0" ) : QStringLiteral( "1" ) );
elem.appendChild( elemNavigationSync ); elem.appendChild( elemNavigationSync );
QDomElement elemDebugSettings = doc.createElement( QStringLiteral( "debug-settings" ) ); QDomElement elemDebugSettings = doc.createElement( QStringLiteral( "debug-settings" ) );
@ -879,11 +882,21 @@ void Qgs3DMapSettings::setRendererUsage( Qgis::RendererUsage rendererUsage )
mRendererUsage = rendererUsage; mRendererUsage = rendererUsage;
} }
void Qgs3DMapSettings::setViewSyncMode( ViewSyncMode mode ) void Qgs3DMapSettings::setViewSyncMode( Qgis::ViewSyncMode mode )
{ {
mViewSyncMode = mode; mViewSyncMode = mode;
} }
void Qgs3DMapSettings::setViewFrustumVisualizationEnabled( bool enabled )
{
if ( mVisualizeViewFrustum != enabled )
{
mVisualizeViewFrustum = enabled;
emit viewFrustumVisualizationEnabledChanged();
}
}
void Qgs3DMapSettings::connectChangedSignalsToSettingsChanged() void Qgs3DMapSettings::connectChangedSignalsToSettingsChanged()
{ {
connect( this, &Qgs3DMapSettings::selectionColorChanged, this, &Qgs3DMapSettings::settingsChanged ); connect( this, &Qgs3DMapSettings::selectionColorChanged, this, &Qgs3DMapSettings::settingsChanged );

View File

@ -57,13 +57,6 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
{ {
Q_OBJECT Q_OBJECT
public: public:
enum ViewSyncMode
{
NoSync, //! No syncronisation will happen
Sync3DTo2D, //! Syncronize 3D view camera to the main map canvas extent
Sync2DTo3D //! Update the 2D main canvas extent to include the viewed area from the 3D view
};
//! Constructor for Qgs3DMapSettings //! Constructor for Qgs3DMapSettings
Qgs3DMapSettings(); Qgs3DMapSettings();
//! Copy constructor //! Copy constructor
@ -603,14 +596,28 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
ViewSyncMode viewSyncMode() const { return mViewSyncMode; } Qgis::ViewSyncMode viewSyncMode() const { return mViewSyncMode; }
/** /**
* Sets the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation) * Sets the view sync mode (used to syncronize the 2D main map canvas and the 3D camera navigation)
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
void setViewSyncMode( ViewSyncMode mode ); void setViewSyncMode( Qgis::ViewSyncMode mode );
/**
* Returns whether the camera's view frustum is visualized on the 2D map canvas
*
* \since QGIS 3.26
*/
bool viewFrustumVisualizationEnabled() const { return mVisualizeViewFrustum; }
/**
* Sets whether the camera's view frustum is visualized on the 2D map canvas
*
* \since QGIS 3.26
*/
void setViewFrustumVisualizationEnabled( bool enabled );
signals: signals:
@ -779,6 +786,13 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
*/ */
void fpsCounterEnabledChanged( bool fpsCounterEnabled ); void fpsCounterEnabledChanged( bool fpsCounterEnabled );
/**
* Emitted when the camera's view frustum visualization on the main 2D map canvas is enabled or disabled
*
* \since QGIS 3.26
*/
void viewFrustumVisualizationEnabledChanged();
private: private:
#ifdef SIP_RUN #ifdef SIP_RUN
Qgs3DMapSettings &operator=( const Qgs3DMapSettings & ); Qgs3DMapSettings &operator=( const Qgs3DMapSettings & );
@ -832,7 +846,8 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec
double mEyeDomeLightingStrength = 1000.0; double mEyeDomeLightingStrength = 1000.0;
int mEyeDomeLightingDistance = 1; int mEyeDomeLightingDistance = 1;
ViewSyncMode mViewSyncMode = ViewSyncMode::NoSync; Qgis::ViewSyncMode mViewSyncMode = Qgis::ViewSyncMode::NoSync;
bool mVisualizeViewFrustum = false;
bool mDebugShadowMapEnabled = false; bool mDebugShadowMapEnabled = false;
Qt::Corner mDebugShadowMapCorner = Qt::Corner::TopLeftCorner; Qt::Corner mDebugShadowMapCorner = Qt::Corner::TopLeftCorner;

View File

@ -353,12 +353,12 @@ void Qgs3DMapCanvas::onNavigationModeHotKeyPressed( QgsCameraController::Navigat
mScene->cameraController()->setCameraNavigationMode( mode ); mScene->cameraController()->setCameraNavigationMode( mode );
} }
void Qgs3DMapCanvas::viewExtent( const QgsRectangle &extent ) void Qgs3DMapCanvas::setViewFrom2DExtent( const QgsRectangle &extent )
{ {
mScene->viewExtent( extent ); mScene->setViewFrom2DExtent( extent );
} }
QgsRectangle Qgs3DMapCanvas::viewFrustum2DExtent() QVector<QgsPointXY> Qgs3DMapCanvas::viewFrustum2DExtent()
{ {
return mScene->viewFrustum2DExtent(); return mScene->viewFrustum2DExtent();
} }

View File

@ -41,7 +41,7 @@ class QgsWindow3DEngine;
class QgsPointXY; class QgsPointXY;
class Qgs3DNavigationWidget; class Qgs3DNavigationWidget;
class QgsTemporalController; class QgsTemporalController;
class QgsRubberBand;
class Qgs3DMapCanvas : public QWidget class Qgs3DMapCanvas : public QWidget
{ {
@ -105,14 +105,14 @@ class Qgs3DMapCanvas : public QWidget
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
void viewExtent( const QgsRectangle &extent ); void setViewFrom2DExtent( const QgsRectangle &extent );
/** /**
* Calculates the 2D extent viewed by the 3D camera * Calculates the 2D extent viewed by the 3D camera as the vertices of the viewed trapezoid
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
QgsRectangle viewFrustum2DExtent(); QVector<QgsPointXY> viewFrustum2DExtent();
signals: signals:
//! Emitted when the 3D map canvas was successfully saved as image //! Emitted when the 3D map canvas was successfully saved as image
@ -131,7 +131,7 @@ class Qgs3DMapCanvas : public QWidget
* *
* \since QGIS 3.26 * \since QGIS 3.26
*/ */
void viewed2DExtentFrom3DChanged( QgsRectangle extent ); void viewed2DExtentFrom3DChanged( QVector<QgsPointXY> extent );
/** /**
* Emitted when the camera navigation \a speed is changed. * Emitted when the camera navigation \a speed is changed.

View File

@ -50,12 +50,14 @@
#include "qgs3dmapexportsettings.h" #include "qgs3dmapexportsettings.h"
#include "qgsdockablewidgethelper.h" #include "qgsdockablewidgethelper.h"
#include "qgsrubberband.h"
#include <QWidget> #include <QWidget>
Qgs3DMapCanvasWidget::Qgs3DMapCanvasWidget( const QString &name, bool isDocked ) Qgs3DMapCanvasWidget::Qgs3DMapCanvasWidget( const QString &name, bool isDocked )
: QWidget( nullptr ) : QWidget( nullptr )
, mCanvasName( name ) , mCanvasName( name )
, mViewFrustumHighlight( nullptr )
{ {
const QgsSettings setting; const QgsSettings setting;
@ -230,6 +232,8 @@ Qgs3DMapCanvasWidget::Qgs3DMapCanvasWidget( const QString &name, bool isDocked )
Qgs3DMapCanvasWidget::~Qgs3DMapCanvasWidget() Qgs3DMapCanvasWidget::~Qgs3DMapCanvasWidget()
{ {
delete mDockableWidgetHelper; delete mDockableWidgetHelper;
if ( mViewFrustumHighlight )
delete mViewFrustumHighlight;
} }
void Qgs3DMapCanvasWidget::resizeEvent( QResizeEvent *event ) void Qgs3DMapCanvasWidget::resizeEvent( QResizeEvent *event )
@ -322,6 +326,8 @@ void Qgs3DMapCanvasWidget::setMapSettings( Qgs3DMapSettings *map )
// Disable button for switching the map theme if the terrain generator is a mesh, or if there is no terrain // Disable button for switching the map theme if the terrain generator is a mesh, or if there is no terrain
mBtnMapThemes->setDisabled( !mCanvas->map()->terrainGenerator() || mCanvas->map()->terrainGenerator()->type() == QgsTerrainGenerator::Mesh ); mBtnMapThemes->setDisabled( !mCanvas->map()->terrainGenerator() || mCanvas->map()->terrainGenerator()->type() == QgsTerrainGenerator::Mesh );
mLabelFpsCounter->setVisible( map->isFpsCounterEnabled() ); mLabelFpsCounter->setVisible( map->isFpsCounterEnabled() );
connect( map, &Qgs3DMapSettings::viewFrustumVisualizationEnabledChanged, this, &Qgs3DMapCanvasWidget::onViewFrustumVisualizationEnabledChanged );
} }
void Qgs3DMapCanvasWidget::setMainCanvas( QgsMapCanvas *canvas ) void Qgs3DMapCanvasWidget::setMainCanvas( QgsMapCanvas *canvas )
@ -331,6 +337,11 @@ void Qgs3DMapCanvasWidget::setMainCanvas( QgsMapCanvas *canvas )
connect( mMainCanvas, &QgsMapCanvas::layersChanged, this, &Qgs3DMapCanvasWidget::onMainCanvasLayersChanged ); connect( mMainCanvas, &QgsMapCanvas::layersChanged, this, &Qgs3DMapCanvasWidget::onMainCanvasLayersChanged );
connect( mMainCanvas, &QgsMapCanvas::canvasColorChanged, this, &Qgs3DMapCanvasWidget::onMainCanvasColorChanged ); connect( mMainCanvas, &QgsMapCanvas::canvasColorChanged, this, &Qgs3DMapCanvasWidget::onMainCanvasColorChanged );
connect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &Qgs3DMapCanvasWidget::onMainMapCanvasExtentChanged ); connect( mMainCanvas, &QgsMapCanvas::extentsChanged, this, &Qgs3DMapCanvasWidget::onMainMapCanvasExtentChanged );
if ( mViewFrustumHighlight )
delete mViewFrustumHighlight;
mViewFrustumHighlight = new QgsRubberBand( canvas, QgsWkbTypes::PolygonGeometry );
mViewFrustumHighlight->setColor( QColor::fromRgba( qRgba( 0, 0, 255, 50 ) ) );
} }
void Qgs3DMapCanvasWidget::resetView() void Qgs3DMapCanvasWidget::resetView()
@ -507,27 +518,50 @@ void Qgs3DMapCanvasWidget::onMainMapCanvasExtentChanged()
{ {
switch ( mCanvas->map()->viewSyncMode() ) switch ( mCanvas->map()->viewSyncMode() )
{ {
case Qgs3DMapSettings::NoSync: case Qgis::ViewSyncMode::NoSync:
break; break;
case Qgs3DMapSettings::Sync3DTo2D: case Qgis::ViewSyncMode::Sync3DTo2D:
mCanvas->viewExtent( mMainCanvas->extent() ); mCanvas->setViewFrom2DExtent( mMainCanvas->extent() );
break; break;
case Qgs3DMapSettings::Sync2DTo3D: case Qgis::ViewSyncMode::Sync2DTo3D:
break; break;
} }
} }
void Qgs3DMapCanvasWidget::onViewed2DExtentFrom3DChanged( QgsRectangle extent ) void Qgs3DMapCanvasWidget::onViewed2DExtentFrom3DChanged( QVector<QgsPointXY> extent )
{ {
switch ( mCanvas->map()->viewSyncMode() ) switch ( mCanvas->map()->viewSyncMode() )
{ {
case Qgs3DMapSettings::NoSync: case Qgis::ViewSyncMode::NoSync:
break; break;
case Qgs3DMapSettings::Sync3DTo2D: case Qgis::ViewSyncMode::Sync3DTo2D:
break; break;
case Qgs3DMapSettings::Sync2DTo3D: case Qgis::ViewSyncMode::Sync2DTo3D:
mMainCanvas->setExtent( extent ); {
QgsRectangle extentRect;
extentRect.setMinimal();
for ( QgsPointXY &pt : extent )
{
extentRect.include( pt );
}
mMainCanvas->setExtent( extentRect );
mMainCanvas->refresh(); mMainCanvas->refresh();
break; break;
}
}
onViewFrustumVisualizationEnabledChanged();
}
void Qgs3DMapCanvasWidget::onViewFrustumVisualizationEnabledChanged()
{
mViewFrustumHighlight->reset( QgsWkbTypes::PolygonGeometry );
if ( mCanvas->map()->viewFrustumVisualizationEnabled() )
{
for ( QgsPointXY &pt : mCanvas->viewFrustum2DExtent() )
{
mViewFrustumHighlight->addPoint( pt, false );
}
mViewFrustumHighlight->closePoints();
} }
} }

View File

@ -34,6 +34,7 @@ class Qgs3DMapToolIdentify;
class Qgs3DMapToolMeasureLine; class Qgs3DMapToolMeasureLine;
class QgsMapCanvas; class QgsMapCanvas;
class QgsDockableWidgetHelper; class QgsDockableWidgetHelper;
class QgsRubberBand;
class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget
{ {
@ -86,7 +87,8 @@ class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget
void currentMapThemeRenamed( const QString &theme, const QString &newTheme ); void currentMapThemeRenamed( const QString &theme, const QString &newTheme );
void onMainMapCanvasExtentChanged(); void onMainMapCanvasExtentChanged();
void onViewed2DExtentFrom3DChanged( QgsRectangle extent ); void onViewed2DExtentFrom3DChanged( QVector<QgsPointXY> extent );
void onViewFrustumVisualizationEnabledChanged();
private: private:
QString mCanvasName; QString mCanvasName;
@ -108,6 +110,7 @@ class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget
QAction *mActionEnableEyeDome = nullptr; QAction *mActionEnableEyeDome = nullptr;
QToolButton *mBtnOptions = nullptr; QToolButton *mBtnOptions = nullptr;
QgsDockableWidgetHelper *mDockableWidgetHelper = nullptr; QgsDockableWidgetHelper *mDockableWidgetHelper = nullptr;
QgsRubberBand *mViewFrustumHighlight = nullptr;
}; };
#endif // QGS3DMAPCANVASWIDGET_H #endif // QGS3DMAPCANVASWIDGET_H

View File

@ -193,6 +193,7 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
edlDistanceSpinBox->setValue( map->eyeDomeLightingDistance() ); edlDistanceSpinBox->setValue( map->eyeDomeLightingDistance() );
mSyncModeComboBox->setCurrentIndex( ( int )map->viewSyncMode() ); mSyncModeComboBox->setCurrentIndex( ( int )map->viewSyncMode() );
mVisualizeExtentCheckBox->setChecked( map->viewFrustumVisualizationEnabled() );
mDebugShadowMapCornerComboBox->addItem( tr( "Top Left" ) ); mDebugShadowMapCornerComboBox->addItem( tr( "Top Left" ) );
mDebugShadowMapCornerComboBox->addItem( tr( "Top Right" ) ); mDebugShadowMapCornerComboBox->addItem( tr( "Top Right" ) );
@ -360,7 +361,8 @@ void Qgs3DMapConfigWidget::apply()
mMap->setEyeDomeLightingStrength( edlStrengthSpinBox->value() ); mMap->setEyeDomeLightingStrength( edlStrengthSpinBox->value() );
mMap->setEyeDomeLightingDistance( edlDistanceSpinBox->value() ); mMap->setEyeDomeLightingDistance( edlDistanceSpinBox->value() );
mMap->setViewSyncMode( static_cast<Qgs3DMapSettings::ViewSyncMode>( mSyncModeComboBox->currentIndex() ) ); mMap->setViewSyncMode( static_cast<Qgis::ViewSyncMode>( mSyncModeComboBox->currentIndex() ) );
mMap->setViewFrustumVisualizationEnabled( mVisualizeExtentCheckBox->isChecked() );
mMap->setDebugDepthMapSettings( mDebugDepthMapGroupBox->isChecked(), static_cast<Qt::Corner>( mDebugDepthMapCornerComboBox->currentIndex() ), mDebugDepthMapSizeSpinBox->value() ); mMap->setDebugDepthMapSettings( mDebugDepthMapGroupBox->isChecked(), static_cast<Qt::Corner>( mDebugDepthMapCornerComboBox->currentIndex() ), mDebugDepthMapSizeSpinBox->value() );
mMap->setDebugShadowMapSettings( mDebugShadowMapGroupBox->isChecked(), static_cast<Qt::Corner>( mDebugShadowMapCornerComboBox->currentIndex() ), mDebugShadowMapSizeSpinBox->value() ); mMap->setDebugShadowMapSettings( mDebugShadowMapGroupBox->isChecked(), static_cast<Qt::Corner>( mDebugShadowMapCornerComboBox->currentIndex() ), mDebugShadowMapSizeSpinBox->value() );

View File

@ -1373,6 +1373,15 @@ class CORE_EXPORT Qgis
}; };
Q_ENUM( RendererUsage ) Q_ENUM( RendererUsage )
enum class ViewSyncMode : int
{
NoSync = 0, //! No syncronisation will happen
Sync3DTo2D = 1, //! Syncronize 3D view camera to the main map canvas extent
Sync2DTo3D = 2, //! Update the 2D main canvas extent to include the viewed area from the 3D view
BothWaysSync = 3
};
Q_ENUM( ViewSyncMode )
/** /**
* History provider backends. * History provider backends.
* *

View File

@ -94,7 +94,7 @@
<string>Terrain settings</string> <string>Terrain settings</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../images/images.qrc"> <iconset>
<normaloff>:/images/themes/default/mLayoutItem3DMap.svg</normaloff>:/images/themes/default/mLayoutItem3DMap.svg</iconset> <normaloff>:/images/themes/default/mLayoutItem3DMap.svg</normaloff>:/images/themes/default/mLayoutItem3DMap.svg</iconset>
</property> </property>
</item> </item>
@ -106,7 +106,7 @@
<string>Lights settings</string> <string>Lights settings</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../images/images.qrc"> <iconset>
<normaloff>:/images/themes/default/mActionHighlightFeature.svg</normaloff>:/images/themes/default/mActionHighlightFeature.svg</iconset> <normaloff>:/images/themes/default/mActionHighlightFeature.svg</normaloff>:/images/themes/default/mActionHighlightFeature.svg</iconset>
</property> </property>
</item> </item>
@ -118,7 +118,7 @@
<string>Shadow settings</string> <string>Shadow settings</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../images/images.qrc"> <iconset>
<normaloff>:/images/themes/default/mIconShadow.svg</normaloff>:/images/themes/default/mIconShadow.svg</iconset> <normaloff>:/images/themes/default/mIconShadow.svg</normaloff>:/images/themes/default/mIconShadow.svg</iconset>
</property> </property>
</item> </item>
@ -130,7 +130,7 @@
<string>Camera and skybox settings</string> <string>Camera and skybox settings</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../images/images.qrc"> <iconset>
<normaloff>:/images/themes/default/mIconCamera.svg</normaloff>:/images/themes/default/mIconCamera.svg</iconset> <normaloff>:/images/themes/default/mIconCamera.svg</normaloff>:/images/themes/default/mIconCamera.svg</iconset>
</property> </property>
</item> </item>
@ -147,7 +147,7 @@
<string>Advanced settings</string> <string>Advanced settings</string>
</property> </property>
<property name="icon"> <property name="icon">
<iconset resource="../../../images/images.qrc"> <iconset>
<normaloff>:/images/themes/default/propertyicons/system.svg</normaloff>:/images/themes/default/propertyicons/system.svg</iconset> <normaloff>:/images/themes/default/propertyicons/system.svg</normaloff>:/images/themes/default/propertyicons/system.svg</iconset>
</property> </property>
</item> </item>
@ -184,7 +184,7 @@
<item> <item>
<widget class="QStackedWidget" name="m3DOptionsStackedWidget"> <widget class="QStackedWidget" name="m3DOptionsStackedWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>4</number> <number>5</number>
</property> </property>
<widget class="QWidget" name="mPageTerrain"> <widget class="QWidget" name="mPageTerrain">
<layout class="QVBoxLayout" name="verticalLayout_61"> <layout class="QVBoxLayout" name="verticalLayout_61">
@ -421,7 +421,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>98</width> <width>77</width>
<height>43</height> <height>43</height>
</rect> </rect>
</property> </property>
@ -595,8 +595,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>234</width> <width>721</width>
<height>220</height> <height>604</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayoutCameraSkybox"> <layout class="QVBoxLayout" name="verticalLayoutCameraSkybox">
@ -723,11 +723,31 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>707</width> <width>710</width>
<height>584</height> <height>584</height>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="gridLayout_7"> <layout class="QGridLayout" name="gridLayout_7">
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Sync mode</string>
</property>
</widget>
</item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="mSyncModeComboBox"> <widget class="QComboBox" name="mSyncModeComboBox">
<item> <item>
@ -747,23 +767,10 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="1" column="1"> <item row="1" column="0" colspan="2">
<spacer name="verticalSpacer"> <widget class="QCheckBox" name="mVisualizeExtentCheckBox">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text"> <property name="text">
<string>Sync mode</string> <string>Visualize viewed 3D area in main 2D map canvas</string>
</property> </property>
</widget> </widget>
</item> </item>
@ -797,7 +804,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>408</width> <width>707</width>
<height>690</height> <height>690</height>
</rect> </rect>
</property> </property>
@ -1199,9 +1206,6 @@
<tabstop>mDebugDepthMapCornerComboBox</tabstop> <tabstop>mDebugDepthMapCornerComboBox</tabstop>
<tabstop>mDebugDepthMapSizeSpinBox</tabstop> <tabstop>mDebugDepthMapSizeSpinBox</tabstop>
</tabstops> </tabstops>
<resources> <resources/>
<include location="../../../images/images.qrc"/>
<include location="../../../images/images.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>